Home

Awesome

TC39 Proposal: Dynamic Code Brand Checks

Table of Contents

Status

Champion(s): koto<br> Author(s): mikesamuel, koto <br> Stage: 3 <br> Spec: ecmarkup output, source

TL;DR

Allow hosts to create code-like objects and change HostEnsureCanCompileStrings( calleeRealm, parameterStrings, bodyString, direct ) to HostEnsureCanCompileStrings( calleeRealm, parameterStrings, bodyString, codeString, compilationType, parameterArgs, bodyArg ).

Motivation

eval is Evil

The eval function is the most misused feature of JavaScript. Avoid it.

-- <cite>Douglas Crockford, "JavaScript: The Good Parts"</cite>

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:

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. Since strings in Javascript programs often come from untrusted sources (DOM-Based XSS in web platform), the trust decisions might be based on brand-checking objects representing the code that the application author trusts to be evaluated.

Trusted Types

The Trusted Types proposal (explainer for TC39</a>) seeks to guard risky operations like dynamic code evaluation by requiring that code portions have a runtime type that indicates that they have been explicitly trusted.

Specifically, when Trusted Types enforcement is turned on, it would like to ensure that arguments to <code>eval()</code> and <code>new Function()</code> are (or are convertible to) a TrustedScript - an object wrapping around a string stored in an internal, immutable slot. This enhances the existing dynamic code evaluation guards that the web platform already has with CSP (Trusted Types also integrates with other CSP mechanisms).

This proposal seeks to solve the following problems:

Problem 1: %eval% does not accept objects in lieu of strings for code

Currently, typeof x === 'string' || eval(x) === x. Eval exits early when its argument is not a string.

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:

Runtime Semantics: PerformEval ( x, evalRealm, strictCaller, direct )

<p>The abstract operation PerformEval with arguments *x*, *evalRealm*, *strictCaller*, and *direct* performs the following steps:</p>
  1. Assert: If direct is false, then strictCaller is also false.
  2. If Type(x) is not String, return x.

Backwards compatibility constraints

To avoid breaking the web, eval(x) should do nothing different when x is a non-string value that existing applications might create.

Solution

Define a spec abstraction, IsCodeLike, that allows some object values through but without changing the semantics of pre-existing programs.

Pros
Cons

Problem 2: Host callout does not receive type information

Currently the information available to decide whether to allow compilation of a string is a realm, a list of strings from parameters, a string from body, and a boolean.

HostEnsureCanCompileStrings( _calleeRealm, parameterStrings, bodyString, direct )

HostEnsureCanCompileStrings is an implementation-defined abstract operation that allows host environments to block certain ECMAScript functions which allow developers to compile strings into ECMAScript code.

For the hosts to be able to guard dynamic code evaluation effectively, additional type information is needed.

Solution

This proposal aims to provide additional context to HostEnsureCanCompileStrings, and reorder the steps in CreateDynamicFunction so that HostEnsureCanCompileStrings receives runtime type information - namely, the eval argument, or all the Function constructor arguments.

Cons

Problem 3: Host callout does not receive the full code to check

HostEnsureCanCompileStrings is called with parameters for the source code, but they're not in a unified string.

Solution

This proposal updates the host callout to contain the full code string to be executed, and moves the callout in CreateDynamicFunction after the function body is assembled.

Tests

Related tests at

but testing these require affecting the behavior of host callouts so will probably need to be specified as web-platform-tests.