My thinking is that just tracking capture isn't really sufficient to
avoid escape of the secret data,
If you look at a language like jeeves, consider say GPS coordinate and
a function that converts that into place name strings, or nearest
address.
It doesn't need to capture the coordinates for the data to escape. To
really avoid the escape you need to do something like refer to secrets
indirectly
and not derive them, or avoid computations containing data derived
from secrets and do some kind of taint tracking.
https://github.com/jeanqasaur/jeeves was one language which tracked
computations derived from secret data.
I've also seen cases which were very similar to timing attacks where
compiler optimizations enabled you to figure out secrets without
the ability to read them directly, e.g. if you had a reference +
interning then you could create a new reference that the attacker
controls
and compare them.
All that said, I think capture is far more important than the
treatment it is typically given in programming language literature
which
typically can be summarized by a section on free and bound variables.
I thought this was an interesting paper.
https://dl.acm.org/doi/10.1145/3618003#Bib0033
Lastly, I really don't like when implementations and
signatures/headers are specified in the same file.
Languages like ML don't actually have *any* visibility modifiers, if
something is specified in a signature/header it is visible.
If it is omitted from the signature it is private. Whenever you have
"public", "private", "sensitive", "hazardous" visibility qualifiers.
There is always a tension that this qualifier has a perspective which
is not signaled by the qualifier itself. Treating
visiblity/capturability in this kind of boolean way
has always in my experience seemed to be an oversimplification that
always ends up confusing. Public or private within which layer of the
onion, the qualifier itself
does not say. It implies that there is a global perspective from which
you can view your program, and every value falls to one or the other
side.
In ML, where you have say `signature FOO = sig end;` for something
with no fields, and `signature BAR = sig val x: u8 end;` for a value
with a single field
you are given the perspective of where a field is visible from, when
viewed through either the FOO or the BAR signature, which promotes
local reasoning...
Sorry, if I rant.