Caja Transformation Rules

Default set of transformations used by Caja

RuleSynopsisReasonMatchesSubstitutes
0moduleEnvelopeCajole a ModuleEnvelope into a call to load a module description.Each script tag defines a separately loadable program unit.<a ModuleEnvelope>___.loadModule(function(___, IMPORTS___) { @body*;});
1moduleDisallow top-level "this". Import free vars. Return last expr-statementIn Caja, "this" may only be bound to an object when within the object's encapsulation boundary. At top-level level, "this" would be bound to the provided imports object, but the module is outside that object's encapsulation boundary.{@ss*;}@importedvars*; @startStmts*; @expanded*;
2syntheticReferencePass through synthetic references.A variable may not be mentionable otherwise./* synthetic */ @ref<expanded>
3syntheticCallsPass through calls where the method name is synthetic.A synthetic method may not be marked callable./* synthetic */ @o.@m(@as*)<expanded>
4syntheticDeletesPass through deletes of synthetic members.A synthetic member may not be marked deletable./* synthetic */ delete @o.@m<expanded>
5syntheticReadsPass through reads of synthetic members.A synthetic member may not be marked readable./* synthetic */ @o.@m<expanded>
6syntheticSetMemberPass through sets of synthetic members.A synthetic member may not be marked writable./* synthetic */ @o.@m = @v<expanded>
7syntheticSetVarPass through set of synthetic vars.A local variable might not be mentionable otherwise./* synthetic */ @lhs = @rhs<expanded>
8syntheticDeclarationPass through synthetic variables which are unmentionable.Synthetic code might need local variables for safe-keeping./* synthetic */ var @v = @initial?;<expanded>
9syntheticFnDeclarationAllow declaration of synthetic functions.Synthetic functions allow generated code to avoid introducing unnecessary scopes./* synthetic */ function @i?(@actuals*) { @body* }<expanded>
10syntheticCatches1Pass through synthetic variables which are unmentionable.Catching unmentionable exceptions helps maintain invariants.try { @body*; } catch (/* synthetic */ @ex___) { @handler*; }try { @body*; } catch (@ex___) { @handler*; }
11syntheticCatches2Pass through synthetic variables which are unmentionable.Catching unmentionable exceptions helps maintain invariants.try { @body*; } catch (/* synthetic */ @ex___) { @handler*; } finally { @cleanup*; }try { @body*; } catch (/* synthetic */ @ex___) { @handler*; } finally { @cleanup*; }
12blockInitialize named functions at the beginning of their enclosing block.Nested named function declarations are illegal in ES3 but are universally supported by all JavaScript implementations, though in different ways. The compromise semantics currently supported by Caja is to hoist the declaration of a variable with the function's name to the beginning of the enclosing function body or module top level, and to initialize this variable to a new anonymous function every time control re-enters the enclosing block. Note that ES3.1 and ES4 specify a better and safer semantics -- block level lexical scoping -- that we'd like to adopt into Caja eventually. However, it so challenging to implement this semantics by translation to currently-implemented JavaScript that we provide something quicker and dirtier for now.{@ss*;}@startStmts*; @ss*;
13withStatically reject if a `with` block is found.`with` violates the assumptions made by Scope, and makes it very hard to write a Scope that works. http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/ briefly touches on why `with` is bad for programmers. For reviewers -- matching of references with declarations can only be done at runtime. All other secure JS subsets that we know of (ADSafe, Jacaranda, & FBJS) also disallow `with`.with (@scope) @body;<reject>
14foreachBadFreeVariableDo not allow a for-in to assign to an imported variable.We do not allow assignments to imports anywhere.for (@k in @o) @ss;
15foreachOnly enumerate Caja-visible and enumerable property names. A for-in on "this" will see pubic and protected property names. Otherwise, only public property names.To enumerate any other property names would be to violate the object's encapsulation, leak internals of the Caja implementation, or violate taming decisions of what should be visible.for (var @k in @o) @ss;<approx> for (@k in @o) { if (___.@canEnum(@o,@k)) @ss
16tryCatchEnsure that only immutable data is thrown, and repair scope confusion in existing JavaScript implementations of try/catch.When manually reviewing code for vulnerability, experience shows that reviewers cannot pay adequate attention to the pervasive possibility of thrown exceptions. These lead to four dangers: 1) leaking an authority-bearing object, endangering integrity, 2) leaking a secret, endangering secrecy, and 3) aborting a partially completed state update, leaving the state malformed, endangering integrity, and 4) preventing an operation that was needed, endangering availability. Caja only seeks to make strong claims about integrity. By ensuring that only immutable (transitively frozen) data is thrown, we prevent problem #1. For the others, programmer vigilance is still needed. Current JavaScript implementations fail, in different ways, to implement the scoping of the catch variable specified in ES3. We translate Caja to JavaScript so as to implement the ES3 specified scoping on current JavaScript implementations.try { @s0*; } catch (@x) { @s1*; }try { @s0*; } catch (ex___) { try { throw ___.tameException(ex___); } catch (@x) { @s1*; } }
17tryCatchFinallyFinally adds no special issues beyond those explained in try/catch.Caja is not attempting to impose determinism, so the reasons for Joe-E to avoid finally do not apply.try { @s0*; } catch (@x) { @s1*; } finally { @s2*; }try { @s0*; } catch (ex___) { try { throw ___.tameException(ex___); } catch (@x) { @s1*; } } finally { @s2*; }
18tryFinallySee bug 383. Otherwise, it's just the trivial translation.try/finally actually seems to work as needed by current JavaScript implementations.try { @s0*; } finally { @s1*; }try { @s0*; } finally { @s1*; }
19varArgsMake all references to the magic "arguments" variable into references to a frozen array containing a snapshot of the actual arguments taken when the function was first entered.ES3 specifies that the magic "arguments" variable is a dynamic ("joined") mutable array-like reflection of the values of the parameter variables. However, te typical usage is to pass it to provide access to one's original arguments -- without the intention of providing the ability to mutate the caller's parameter variables. By making a frozen array snapshot with no "callee" property, we provide the least authority assumed by this typical use. The snapshot is made with a "var a___ = ___.args(arguments);" generated at the beginning of the function body.argumentsa___
20varThisTranslates all occurrences of "this" to "t___".The translation is able to worry less about the complex scoping rules of "this". In a function mentioning "this", a "var t___ = this;" is generated at the beginning of the function body.thist___
21varBadSuffixStatically reject if a variable with `__` suffix is found.Caja reserves the `__` suffix for internal use.@v__<reject>
22varBadSuffixDeclarationStatically reject if a variable with `__` suffix is found.Caja reserves the `__` suffix for internal use.<approx>(var|function) @v__ ...<reject>
23varBadImportSuffixStatically reject if an imported variable with `_` suffix is foundA module is outside the encapsulation boundary of its imports object, and so cannot address any of that object's protected properties.@import_<reject>
24varFuncFreezeAn escaping occurence of a function name freezes the function.By adopting this static rule, we only need to generate freezes for names that are statically known to be function names, rather than freezing at every potential point of use.@fname___.primFreeze(@fname)
25varDefaultAny remaining uses of a variable name are preserved.@v@v
26readBadSuffixStatically reject if a property has `__` suffix is found.Caja reserves the `__` suffix for internal use.@x.@p__<reject>
27readInternalRead a public or protected property.Since it is addressed from "this.", Caja assumes we are inside the encapsulation boundary of the object bound to "this", and so its protected properties should be accessible.this.@p<approx> ___.readProp(t___, @'p')
28readBadInternalStatically reject public reading of a property ending with '_'.Caja defines variable with a `_` suffix as protected.@x.@p_<reject>
29readPublic@o.@p<approx> ___.readPub(@o, @'p')
30readIndexInternalthis[@s]___.readProp(t___, @s)
31readIndexPublic@o[@s]___.readPub(@o, @s)
32setBadAssignToFunctionNameStatically reject if an assignment expression assigns to a function name.<approx> @fname @op?= @x<reject>
33setBadThisStatically reject if an expression assigns to `this`.Invalid JavaScript.this = @z<reject>
34setBadFreeVariableStatically reject if an expression assigns to a free variable.This is still controversial (see bug 375). However, the rationale is to prevent code that's nested lexically within a module to from introducing mutable state outside its local function-body scope. Without this rule, two nested blocks within the same module could communicate via a pseudo-imported variable that is not declared or used at the outer scope of the module body.@import = @y<reject>
35setBadValueOfStatically reject if assigning to valueOf.We depend on valueOf returning consistent results.@x.valueOf = @z<reject>
36setBadSuffixStatically reject if a property with `__` suffix is found.Caja reserves the `__` suffix for internal use.@x.@p__ = @z<reject>
37setInternalSet or create a public or protected property.We allow methods and constructors within a constructed object to create new properties on itself directly by assignment.this.@p = @r<approx> ___.setProp(t___, @'p', @r)
38setMemberInitialize a member of the prototypical object associated with a constructor or named function, to be inherited by the instances of that function.The right hand side of this rule is a "method context" -- a position in which Caja methods can appear. This allows unattached methods to be stored in the prototypical object, which is necessary for allowing instances to share these. However, any attempt to obtain access to a method as a value will obtain at most an attached method.@df.prototype.@p = @m___.setMember(@df, @'p', @m)
39setBadInternalCannot publicly access a property ending with '_'.Caja defines variable with a `_` suffix as protected.@x.@y_ = @z<reject>
40setStaticInitialize the direct properties (static members) of a potentially-mutable constructor or named function.@fname.@p = @r___.setStatic(@fname, @'p', @r)
41setPublicSet a public property.If the object is an unfrozen JSONContainer (a record or array), then this will create the own property if needed. If it is an unfrozen constructed object, then clients can assign to existing public own properties, but cannot directly create such properties.@o.@p = @r<approx> ___.setPub(@o, @'p', @r);
42setIndexInternalthis[@s] = @r___.setProp(t___, @s, @r)
43setIndexPublic@o[@s] = @r___.setPub(@o, @s, @r)
44setBadInitializeStatically reject if a variable with `__` suffix is found.Caja reserves the `__` suffix for internal use.var @v__ = @r<reject>
45setInitializeEnsure v is not a function name. Expand the right side.var @v = @rvar @v = @r
46setBadDeclareStatically reject if a variable with `__` suffix is found.Caja reserves the `__` suffix for internal use.var @v__<reject>
47setDeclareEnsure that v isn't a function name.var @vvar @v
48setBadVarStatically reject if a variable with `__` suffix is found.Caja reserves the `__` suffix for internal use.@v__ = @r<reject>
49setVarOnly if v isn't a function name.@v = @r@v = @r
50setReadModifyWriteLocalVar<approx> @x @op= @y<approx> @x = @x @op @y
51setIncrDecrHandle pre and post ++ and --.<approx> ++@x but any {pre,post}{in,de}crement will do<UNKNOWN>
52newCalllessCtorAdd missing empty argument list.JavaScript syntax allows constructor calls without "()".new @ctornew @ctor()
53newCtornew @ctor(@as*)new (___.asCtor(@ctor))(@as*)
54deleteBadValueOfProhibit deletion of valueOf.Although a non-existent valueOf should behave the same way asthe default one as regards [[DefaultValue]], for simplicity weonly want to have to consider one of those cases.delete @o.valueOf<reject>
55deleteBadSuffixdelete @o.@p__<reject>
56deleteInternaldelete this.@p___.deleteProp(t___, @'p')
57deleteBadInternaldelete @o.@p_<reject>
58deletePublicdelete @o.@p___.deletePub(@o, @'p')
59deleteIndexInternaldelete this[@s]___.deleteProp(t___, @s)
60deleteIndexPublicdelete @o[@s]___.deletePub(@o, @s)
61deleteNonPropertydelete @v<reject>
62callBadSuffixStatically reject if a selector with `__` suffix is found.Caja reserves the `__` suffix for internal use.@o.@p__(@as*)<reject>
63callInternalthis.@p(@as*)<approx> ___.callProp(t___, @'p', [@as*]
64callBadInternalStatically reject if a public selector with `_` suffix is found.Caja defines selectors with a `_` as private.@o.@s_(@as*)<reject>
65callCajaDef2Declares that the first argument acts as a derived constructor inheriting from the second.Sets up a well formed prototype inheritance chain between these two functions. The first argument must be a declared function name. Calling caja.def() on it does not freeze it.caja.def(@fname, @base)caja.def(@fname, @base)
66callCajaDef2BadFunctionReject calls to caja.def() on names of functions statically known to be frozen.Within a function foo(), foo must already be frozen, so it is too late to initialize it.caja.def(@fname, @base)<reject>
67callCajaDef2BadReject other calls to caja.def().If the first argument is not a declared function name, then it cannot be an unfrozen function.caja.def(@x, @base)<reject>
68callCajaDef3PlusDeclare an inheritance relationship, and initialize methods and statics.The enumerable own properties of the third and fourth arguments, if present are used to initialize @fname.prototype and @fname, respectively. The third argument must statically be an object-literal expression. The value positions of this expression is a method context -- a position in which methods are allowed.caja.def(@fname, @base, @mm, @ss?)caja.def(@fname, @base, @mm, @ss?)
69callCajaDef3PlusBadFunctionReject initialization of a name of a function statically known to be frozen.caja.def(@fname, @base, @mm, @ss?)<reject>
70callCajaDef3PlusBadReject other calls to caja.def().caja.def(@x, @base, @mm, @ss?)<reject>
71callFuncInlineMethodCall(function (@formals*) { @body*; }).call(this, @args*);(function (@formals*) { @fh*; @stmts*; @body*; }) .call(this, @args*);
72callFuncInlineMethodApply(function (@formals*) { @body*; }).apply(this, @arg);(function (@formals*) { @fh*; @stmts*; @body*; }) .apply(this, @arg);
73callFuncInlineMethodBind(function (@formals*) { @body*; }).bind(this, @args*);___.simpleFrozenFunc(function (@formals*) { @fh*; @stmts*; @body*; }.bind(t___, @args*));
74permittedCall@o.@m(@as*)<approx> @o.@m(@as*)
75callPublic@o.@m(@as*)<approx> ___.callPub(@o, @'m', [@as*])
76callIndexInternalthis[@s](@as*)___.callProp(t___, @s, [@as*])
77callIndexPublic@o[@s](@as*)___.callPub(@o, @s, [@as*])
78callFunc@f(@as*)___.asSimpleFunc(@f)(@as*)
79funcAnonSimplefunction (@ps*) { @bs*; }___.simpleFrozenFunc( function (@ps*) { @fh*; @stmts*; @bs*; })
80funcNamedSimpleDeclfunction @fname(@ps*) { @bs*; }@fname = ___.simpleFunc( function(@ps*) { @fh*; @stmts*; @bs*; }, @'fname');
81funcNamedSimpleValuefunction @fname(@ps*) { @bs*; }(function() { function @fname(@ps*) { @fh*; @stmts*; @bs*; } return ___.simpleFrozenFunc(@fname, @'fname'); })();
82funcXo4aRewrites an 1) anonymous function 2) mentioning this 3) whose earliest function scope ancestor is NOT a constructor or method into an exophoric function.A moderately risky stepping stone to ease the conversion of old code.(function (@formals*) { @body*; })<approx> ___.xo4a(function (@formals*) { @fh*; @stmts*; @body*; })
83funcCtorfunction @fname(@ps*) { @b; @bs*; }<approx> @fname = (function () { ___.splitCtor(@fRef, @f_init___Ref); function @fname(var_args) { return new @fRef.make___(arguments); } function @f_init(@ps*) { @fh*; @stmts*; @b; @bs*; } return @fRef; })()
84mapBadKeyValueOfStatically reject 'valueOf' as a keyWe depend on valueOf returning consistent results.({@keys*: @vals*})<reject>
85mapBadKeySuffixStatically reject if a key with `_` suffix is found({@keys*: @vals*})<reject>
86mapNonEmpty({@keys*: @vals*})___.initializeMap([@items*]) where items are interleaved keys and vals
87multiDeclarationConsider declarations separately from initializersvar @a=@b?, @c=@d*{ @decl; @init; }
88otherTypeofTypeof translates simplyOne of Caja's deviations from JavaScript is that reading a non-existent imported variable returns 'undefined' rather than throwing a ReferenceError. Therefore, in Caja, 'typeof' can always evaluate its argument.typeof @ftypeof @f)
89inInternalIs a public or protected property present on 'this'?@i in this___.canReadProp(t___, @i)
90inPublicIs a public property present on the object?@i in @o___.inPub(@i, @o)
91voidOpvoid @xvoid @x
92commaOp(@a, @b)(@a, @b)
93labeledStatementStatically reject if a label with `__` suffix is foundCaja reserves the `__` suffix for internal use@lbl: @stmt;@lbl: @stmt;
94regexLiteralUse the regular expression constructorSo that every use of a regex literal creates a new instance to prevent state from leaking via interned literals. This is consistent with the way ES4 treates regex literals./foo/new ___.RegExp(@pattern, @modifiers?)
95recurseAutomatically recurse into some structures<many><UNKNOWN>