* "Innocent" code is code which we assume to be ignorant of Caja,
* not to be actively hostile, but which may be buggy (and
* therefore accidentally harmful or exploitable). This
* corresponds to legacy code, such as libraries, that we decide
* to run untranslated, perhaps hidden or tamed, but which needs
* to co-exist smoothly with the Caja runtime.
*
* An earlier version of canInnocentEnum() filtered out exactly those
* names ending with a double underbar. It now filters out exactly
* those names ending in a triple underbar. Caja code can't see names
* ending in a double underbar, since existing platforms (like
* Firefox) use such names for purposes that should be hidden from
* Caja code. However, it is not up to Caja to shield innocent code
* from seeing such platform properties. All the magic names Caja
* adds for its own internal bookkeeping end in triple underbar, so
* that is all we need to hide from innocent code.
*/
function canInnocentEnum(obj, name) {
name = String(name);
if (endsWith(name, '___')) { return false; }
return true;
}
Obviously, such manual manipulation of existing code is not scalable.
It seems there are two ways forward:
1) We can release a much simpler "lite-translator" for innocent code,
which would make only two transformations:
* Transform every for/in loop to skip the body for keys that end in
double underbar. As an example, the attached jquery.js differs from
the original only by guarding each for/in loop body with "if
(___.canInnocentEnum(obj, key)) {...}".
* JavaScript's insane proclivity for binding "this" to the global
object creates a pervasive danger of privilege escalation attacks,
even when this occurs only in innocent code. So we would transform
every function that mentions "this" to begin with a test enforcing
that its "this" is not bound to the global object. Ideally, we would
prevent such privilege escalation by static analysis, but JavaScript
makes that impractical.
2) We can alter the Caja runtime so that it no longer stores its
bookkeeping information as properties added to the primordial objects.
To gain the advantages of this approach, we'd have to avoid adding
even a single such property. But Javascript also doesn't have any
hashtable/map datatype that can use objects as keys. If the issue were
just Object.prototype itself, we could just make a special case for
it. But the issue arises for all primordial objects. I see no easy and
affordable way to achieve option #2.
So questions:
* Is there some good way to achieve #2 that I'm missing?
* How bad would it be if we required all innocent code to pass through
our lite translator?
* Is there a third alternative that I'm missing?
--
Text by me above is hereby placed in the public domain
Cheers,
--MarkM