| Rule | Synopsis | Reason | Matches | Substitutes |
| 0 | moduleEnvelope | Cajole 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*;}); |
| 1 | module | Disallow top-level "this". Import free vars. Return last expr-statement | In 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*; |
| 2 | syntheticReference | Pass through synthetic references. | A variable may not be mentionable otherwise. | /* synthetic */ @ref | <expanded> |
| 3 | syntheticCalls | Pass through calls where the method name is synthetic. | A synthetic method may not be marked callable. | /* synthetic */ @o.@m(@as*) | <expanded> |
| 4 | syntheticDeletes | Pass through deletes of synthetic members. | A synthetic member may not be marked deletable. | /* synthetic */ delete @o.@m | <expanded> |
| 5 | syntheticReads | Pass through reads of synthetic members. | A synthetic member may not be marked readable. | /* synthetic */ @o.@m | <expanded> |
| 6 | syntheticSetMember | Pass through sets of synthetic members. | A synthetic member may not be marked writable. | /* synthetic */ @o.@m = @v | <expanded> |
| 7 | syntheticSetVar | Pass through set of synthetic vars. | A local variable might not be mentionable otherwise. | /* synthetic */ @lhs = @rhs | <expanded> |
| 8 | syntheticDeclaration | Pass through synthetic variables which are unmentionable. | Synthetic code might need local variables for safe-keeping. | /* synthetic */ var @v = @initial?; | <expanded> |
| 9 | syntheticFnDeclaration | Allow declaration of synthetic functions. | Synthetic functions allow generated code to avoid introducing unnecessary scopes. | /* synthetic */ function @i?(@actuals*) { @body* } | <expanded> |
| 10 | syntheticCatches1 | Pass through synthetic variables which are unmentionable. | Catching unmentionable exceptions helps maintain invariants. | try { @body*; } catch (/* synthetic */ @ex___) { @handler*; } | try { @body*; } catch (@ex___) { @handler*; } |
| 11 | syntheticCatches2 | Pass 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*; } |
| 12 | block | Initialize 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*; |
| 13 | with | Statically 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> |
| 14 | foreachBadFreeVariable | Do not allow a for-in to assign to an imported variable. | We do not allow assignments to imports anywhere. | for (@k in @o) @ss; | |
| 15 | foreach | Only 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 |
| 16 | tryCatch | Ensure 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*;
}
} |
| 17 | tryCatchFinally | Finally 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*;
} |
| 18 | tryFinally | See 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*; } |
| 19 | varArgs | Make 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. | arguments | a___ |
| 20 | varThis | Translates 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. | this | t___ |
| 21 | varBadSuffix | Statically reject if a variable with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | @v__ | <reject> |
| 22 | varBadSuffixDeclaration | Statically reject if a variable with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | <approx>(var|function) @v__ ... | <reject> |
| 23 | varBadImportSuffix | Statically reject if an imported variable with `_` suffix is found | A module is outside the encapsulation boundary of its imports object, and so cannot address any of that object's protected properties. | @import_ | <reject> |
| 24 | varFuncFreeze | An 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) |
| 25 | varDefault | Any remaining uses of a variable name are preserved. | | @v | @v |
| 26 | readBadSuffix | Statically reject if a property has `__` suffix is found. | Caja reserves the `__` suffix for internal use. | @x.@p__ | <reject> |
| 27 | readInternal | Read 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') |
| 28 | readBadInternal | Statically reject public reading of a property ending with '_'. | Caja defines variable with a `_` suffix as protected. | @x.@p_ | <reject> |
| 29 | readPublic | | | @o.@p | <approx> ___.readPub(@o, @'p') |
| 30 | readIndexInternal | | | this[@s] | ___.readProp(t___, @s) |
| 31 | readIndexPublic | | | @o[@s] | ___.readPub(@o, @s) |
| 32 | setBadAssignToFunctionName | Statically reject if an assignment expression assigns to a function name. | | <approx> @fname @op?= @x | <reject> |
| 33 | setBadThis | Statically reject if an expression assigns to `this`. | Invalid JavaScript. | this = @z | <reject> |
| 34 | setBadFreeVariable | Statically 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> |
| 35 | setBadValueOf | Statically reject if assigning to valueOf. | We depend on valueOf returning consistent results. | @x.valueOf = @z | <reject> |
| 36 | setBadSuffix | Statically reject if a property with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | @x.@p__ = @z | <reject> |
| 37 | setInternal | Set 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) |
| 38 | setMember | Initialize 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) |
| 39 | setBadInternal | Cannot publicly access a property ending with '_'. | Caja defines variable with a `_` suffix as protected. | @x.@y_ = @z | <reject> |
| 40 | setStatic | Initialize the direct properties (static members) of a potentially-mutable constructor or named function. | | @fname.@p = @r | ___.setStatic(@fname, @'p', @r) |
| 41 | setPublic | Set 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); |
| 42 | setIndexInternal | | | this[@s] = @r | ___.setProp(t___, @s, @r) |
| 43 | setIndexPublic | | | @o[@s] = @r | ___.setPub(@o, @s, @r) |
| 44 | setBadInitialize | Statically reject if a variable with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | var @v__ = @r | <reject> |
| 45 | setInitialize | Ensure v is not a function name. Expand the right side. | | var @v = @r | var @v = @r |
| 46 | setBadDeclare | Statically reject if a variable with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | var @v__ | <reject> |
| 47 | setDeclare | Ensure that v isn't a function name. | | var @v | var @v |
| 48 | setBadVar | Statically reject if a variable with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | @v__ = @r | <reject> |
| 49 | setVar | Only if v isn't a function name. | | @v = @r | @v = @r |
| 50 | setReadModifyWriteLocalVar | | | <approx> @x @op= @y | <approx> @x = @x @op @y |
| 51 | setIncrDecr | Handle pre and post ++ and --. | | <approx> ++@x but any {pre,post}{in,de}crement will do | <UNKNOWN> |
| 52 | newCalllessCtor | Add missing empty argument list. | JavaScript syntax allows constructor calls without "()". | new @ctor | new @ctor() |
| 53 | newCtor | | | new @ctor(@as*) | new (___.asCtor(@ctor))(@as*) |
| 54 | deleteBadValueOf | Prohibit 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> |
| 55 | deleteBadSuffix | | | delete @o.@p__ | <reject> |
| 56 | deleteInternal | | | delete this.@p | ___.deleteProp(t___, @'p') |
| 57 | deleteBadInternal | | | delete @o.@p_ | <reject> |
| 58 | deletePublic | | | delete @o.@p | ___.deletePub(@o, @'p') |
| 59 | deleteIndexInternal | | | delete this[@s] | ___.deleteProp(t___, @s) |
| 60 | deleteIndexPublic | | | delete @o[@s] | ___.deletePub(@o, @s) |
| 61 | deleteNonProperty | | | delete @v | <reject> |
| 62 | callBadSuffix | Statically reject if a selector with `__` suffix is found. | Caja reserves the `__` suffix for internal use. | @o.@p__(@as*) | <reject> |
| 63 | callInternal | | | this.@p(@as*) | <approx> ___.callProp(t___, @'p', [@as*] |
| 64 | callBadInternal | Statically reject if a public selector with `_` suffix is found. | Caja defines selectors with a `_` as private. | @o.@s_(@as*) | <reject> |
| 65 | callCajaDef2 | Declares 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) |
| 66 | callCajaDef2BadFunction | Reject 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> |
| 67 | callCajaDef2Bad | Reject 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> |
| 68 | callCajaDef3Plus | Declare 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?) |
| 69 | callCajaDef3PlusBadFunction | Reject initialization of a name of a function statically known to be frozen. | | caja.def(@fname, @base, @mm, @ss?) | <reject> |
| 70 | callCajaDef3PlusBad | Reject other calls to caja.def(). | | caja.def(@x, @base, @mm, @ss?) | <reject> |
| 71 | callFuncInlineMethodCall | | | (function (@formals*) { @body*; }).call(this, @args*); | (function (@formals*) { @fh*; @stmts*; @body*; })
.call(this, @args*); |
| 72 | callFuncInlineMethodApply | | | (function (@formals*) { @body*; }).apply(this, @arg); | (function (@formals*) { @fh*; @stmts*; @body*; })
.apply(this, @arg); |
| 73 | callFuncInlineMethodBind | | | (function (@formals*) { @body*; }).bind(this, @args*); | ___.simpleFrozenFunc(function (@formals*) {
@fh*; @stmts*; @body*;
}.bind(t___, @args*)); |
| 74 | permittedCall | | | @o.@m(@as*) | <approx> @o.@m(@as*) |
| 75 | callPublic | | | @o.@m(@as*) | <approx> ___.callPub(@o, @'m', [@as*]) |
| 76 | callIndexInternal | | | this[@s](@as*) | ___.callProp(t___, @s, [@as*]) |
| 77 | callIndexPublic | | | @o[@s](@as*) | ___.callPub(@o, @s, [@as*]) |
| 78 | callFunc | | | @f(@as*) | ___.asSimpleFunc(@f)(@as*) |
| 79 | funcAnonSimple | | | function (@ps*) { @bs*; } | ___.simpleFrozenFunc(
function (@ps*) {
@fh*;
@stmts*;
@bs*;
}) |
| 80 | funcNamedSimpleDecl | | | function @fname(@ps*) { @bs*; } | @fname = ___.simpleFunc(
function(@ps*) {
@fh*;
@stmts*;
@bs*;
}, @'fname'); |
| 81 | funcNamedSimpleValue | | | function @fname(@ps*) { @bs*; } | (function() {
function @fname(@ps*) {
@fh*;
@stmts*;
@bs*;
}
return ___.simpleFrozenFunc(@fname, @'fname');
})(); |
| 82 | funcXo4a | Rewrites 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*; }) |
| 83 | funcCtor | | | function @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;
})() |
| 84 | mapBadKeyValueOf | Statically reject 'valueOf' as a key | We depend on valueOf returning consistent results. | ({@keys*: @vals*}) | <reject> |
| 85 | mapBadKeySuffix | Statically reject if a key with `_` suffix is found | | ({@keys*: @vals*}) | <reject> |
| 86 | mapNonEmpty | | | ({@keys*: @vals*}) | ___.initializeMap([@items*]) where items are interleaved keys and vals |
| 87 | multiDeclaration | Consider declarations separately from initializers | | var @a=@b?, @c=@d* | { @decl; @init; } |
| 88 | otherTypeof | Typeof translates simply | One 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 @f | typeof @f) |
| 89 | inInternal | Is a public or protected property present on 'this'? | | @i in this | ___.canReadProp(t___, @i) |
| 90 | inPublic | Is a public property present on the object? | | @i in @o | ___.inPub(@i, @o) |
| 91 | voidOp | | | void @x | void @x |
| 92 | commaOp | | | (@a, @b) | (@a, @b) |
| 93 | labeledStatement | Statically reject if a label with `__` suffix is found | Caja reserves the `__` suffix for internal use | @lbl: @stmt; | @lbl: @stmt; |
| 94 | regexLiteral | Use the regular expression constructor | So 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?) |
| 95 | recurse | Automatically recurse into some structures | | <many> | <UNKNOWN> |