Provide HostEnsureCanCompileString implementors more context
Originally floated as a strawman
evalis EvilThe
evalfunction is the most misused feature of JavaScript. Avoid it.-- Douglas Crockford, "JavaScript: The Good Parts"
eval and its friend new Function are problematic because, too
often, an attacker can turn it against the application.
Most code avoids eval, but JS programs are no longer small, and
self-contained as they were when Crock wrote that.
If one module uses eval, even if it's as simple as
Function('return this')() to get a handle to the
global object then eval has to work.
This prevents the use of security measures like:
- Content-Security-Policy
node --disallow_code_generation_from_strings
which turn off eval globally.
As JavaScript programs get larger, the chance that no part of it needs eval or Function()
to operate gets smaller.
It is difficult in JavaScript for a code reviewer to determine that
code never uses these operators. For example, the below can when x is constructor.
({})[x][x](y)()
// ({})[x] === Object
// ({})[x][x] === Function
// ({})[x][x](y) ~ Function(y)
So code review and developer training are unlikely to prevent abuse of these operators.
This aims to solve the problem by providing more context to host environments so that they can make finer-grained trust decisions.
The Trusted Types proposal aims to allow JavaScript development to scale securely.
It makes it easy to separate:
- the decisions to trust a chunk of code to load.
- the check that an input to a sensitive operator like
evalis trustworthy.
This allows trust decisions tto be made where the maximum context is available and allows these decisions to be concentrated in small amounts of thoroughly reviewed code
The checks can also be moved into host code so that they reliably happen before irrevocable actions like code loading complete.
Specifically, Trusted Types would like to require that the code portion of the inputs to %Function% and %eval% are TrustedScript.
Fixes #938.
I'd like to schedule this for a needs_consensus slot per https://github.com/tc39/proposals/pull/209#discussion_r271809863
This should be a semantics free change from the ES side.
Tracking issue filed on the HTML5 side: https://github.com/whatwg/html/issues/4501
How might one specify tests?
AFAIK, there's no way for a test262 implementation to specify that a test runs in the context of a less-than-completely permissive host callout. https://github.com/tc39/test262/issues/2120
Would it be sufficient to include a prose section on what to test like the below?
Run in the context of a HostEnsureCanCompileStrings callout that does blah blah blah:
// test code here
like https://gist.github.com/mikesamuel/5c71584fc10a603167111e933cdc650d
Fixes #938
It may indeed not be possible to test; that's something i think the test262 maintainers can help confirm.
I think you'd need more changes than just this. Step 2 of PerformEval will not evaluate non-String arguments, so it wouldn't really be very useful to apply with a TrustedScript.
Edit: I see, this is covered by https://github.com/tc39/ecma262/issues/1495 Edit 2: Actually, it doesn't.
@littledan, I put together https://github.com/mikesamuel/evalable#tc39-proposal-evalable-stage-0 based on https://github.com/tc39/ecma262/issues/938#issuecomment-457352474
Question. Do we even need to callout to HostEnsureCanCompileStrings for the case of new Function() (with no params)? It doesn't seem to me that new Function() on it's own can be used as a vector for XSS attacks.
@Jamesernator
You're right that (new Function) is not an XSS risk but I suspect I'm missing something more in your question.
IIUC new Function reaches CreateDynamicFunction with zero arguments which reaches 19.2.1.1.1.1 step 13
- If argCount = 0, let bodyText be the empty String.
so new Function('') and (new Function) are equivalent and both are already guarded by the host callout.
It just seems surprising to me that new Function() needs to invoke the machinery for checking whether or not a string is safe given that it wasn't given a string in the first place. (Given that eval(nonString) doesn't bother invoking the HostEnsureCanCompileStrings machinery when it's not given a string).
We would try to revamp this PR (cc @ptomato)
I've opened a rebased version of this over at https://github.com/tc39/ecma262/pull/3222.
@ptomato should we close this, or do you prefer to keep it around for when the time comes to work on trusted types?
If it should be closed, it’d be done on merging of the replacement, I’d expect.
Note that the other PR only replaces part of the motivation of this one. If we'll want to ever support trusted types, we'll need to also pass the uncoerced objects to the host (which is what this PR does).