This proposal has merged into dynamic-code-brand-checks
Relax the requirement that the argument to eval be a string in a non-breaking way.
Currently, typeof x === 'string' || eval(x) === x
.
For example:
console.log(eval(123) === 123);
const array = [ 'alert(1)' ];
console.log(eval(array) === array); // Does not alert
const touchy = { toString() { throw new Error(); } };
console.log(eval(touchy) === touchy); // Does not throw.
This follows from step 2 of the definition of PerformEval:
The abstract operation PerformEval with arguments *x*, *evalRealm*, *strictCaller*, and *direct* performs the following steps:
- Assert: If direct is false, then strictCaller is also false.
- If Type(x) is not String, return x.
Encourage source to sink checking of arguments to eval
to reduce the risk of XSS.
Source to sink checking means we do something at the source of a value so that, when the value
reaches a "sensitive sink" (like eval
), we can double-check that it is safe.
The Trusted Types proposal brings source-to-sink checks to browser built-ins
like .innerHTML
.
- Introduce a number of types that correspond to the XSS sinks we wish to protect.
TrustedScript: This type would be used to represent a trusted JavaScript code block i.e. something that is trusted by the author to be executed by ... passing to an
eval
function family.
Enumerate all the XSS sinks we wish to protect. ...
Introduce a mechanism for disabling the raw string version of each of the sinks identified. ...
As valid trusted type objects must originate from a policy, those policies alone form the trusted codebase in regards to DOM XSS.
So by double-checking at sinks like eval
we can ensure that
only carefully reviewed code that is the source of TrustedScript
values will load alongside trusted code.
This won't work today because, as explained above, eval(myTrustedScript)
does not actually
evaluated the textual content of TrustedScript values.
This proposal is useful in the context of other planned changes:
- The Trusted Types proposal as explained.
- A proposed tweak to the HostEnsureCanCompileStrings
that would allow browsers to take more context into account when deciding
whether to allow a particular call to
eval
.
To avoid breaking the web, eval(x)
should do nothing different when x is a
non-string value that existing applications might create.
This should be the case regardless of whether HostEnsureCanCompileStrings says yes or no as demonstrated in node:
$ npx node@10 --disallow_code_generation_from_strings -e 'console.log(eval(() => {}))'
[Function]
$ npx node@10 --disallow_code_generation_from_strings -e 'console.log(eval("() => {}"))'
[eval]:1
console.log(eval("() => {}"))
^
EvalError: Code generation from strings disallowed for this context
When eval
is passed a non-string value, even though the Host callback rejects all
attempts to eval
, it doesn't throw when given the value () => {}
, but does when
given the string "() => {}"
.
You can browse the ecmarkup output or browse the source.
All the approaches below do the following
- Define IsCodeLike(x), a spec abstraction that returns true for strings so is backwards compatible, and returns true for some other values. (See details of specific proposals below.)
- Tweak PerformEval, which is called by both direct and indirect
eval
, to use IsCodeLike(x) instead of the existing (Type(x) is String). - Use ToString(x) for the code to parse.
- IsCodeLike(x) additionally returns true when x is an object that has an internal slot [[CodeLike]].
- IsCodeLike has no observable side effect for values so is backwards compatible when a program produces no code like values.
From ecma262 issue #938:
HostEnsureCanCompileStrings
Perhaps we could tweak theeval
algo so that if the input is a string or has a particular symbol property then it is stringified to avoid breaking code that depends on the current behavior whereeval
is identity except for strings.
- Define a well-known symbol, @@evalable.
- IsCodeLike(x) additionally returns true when x is an object and
x[Symbol.evalable]
is truthy. Note: using a new symbol makes IsCodeLike backwards comparible for programs that do not useSymbol.evalable
.
- Code-like values are proxyable. (TODO: is this a feature?)
- User code can define code-like values. (TODO: is this a feature?)
- Can be tested in test262 via user code.
TODO: Updates to