Skip to content

Commit 5ed041d

Browse files
committed
Add eval() and Function() consistency
1 parent fc306e7 commit 5ed041d

File tree

2 files changed

+108
-8
lines changed
  • files/en-us/web/javascript/reference/global_objects

2 files changed

+108
-8
lines changed

files/en-us/web/javascript/reference/global_objects/eval/index.md

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ sidebar: jssidebar
77
---
88

99
> [!WARNING]
10-
> Executing JavaScript from a string is an enormous security risk. It is far too easy for a bad actor to run arbitrary code when you use `eval()`. See [Never use direct eval()!](#never_use_direct_eval!), below.
10+
> The argument passed to this method is dynamically evaluated and executed as JavaScript.
11+
> APIs like this are known as [injection sinks](/en-US/docs/Web/API/Trusted_Types_API#concepts_and_usage), and are potentially a vector for [cross-site-scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks.
12+
>
13+
> You can mitigate this risk by always passing {{domxref("TrustedScript")}} objects instead of strings and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types).
14+
>
15+
> See [Security considerations](#security_considerations) for more information.
1116
1217
The **`eval()`** function evaluates JavaScript code represented as a string and returns its completion value. The source is parsed as a script.
1318

@@ -36,15 +41,22 @@ eval(script)
3641
### Parameters
3742

3843
- `script`
39-
- : A string representing a JavaScript expression, statement, or sequence of statements. The expression can include variables and properties of existing objects. It will be parsed as a script, so [`import`](/en-US/docs/Web/JavaScript/Reference/Statements/import) declarations (which can only exist in modules) are not allowed.
44+
- : A {{domxref("TrustedScript")}} instance or string representing a JavaScript expression, statement, or sequence of statements.
45+
The expression can include variables and properties of existing objects. It will be parsed as a script, so [`import`](/en-US/docs/Web/JavaScript/Reference/Statements/import) declarations (which can only exist in modules) are not allowed.
4046

4147
### Return value
4248

43-
The completion value of evaluating the given code. If the completion value is empty, {{jsxref("undefined")}} is returned. If `script` is not a string primitive, `eval()` returns the argument unchanged.
49+
The completion value of evaluating the given code. If the completion value is empty, {{jsxref("undefined")}} is returned.
50+
If `script` is not a {{domxref("TrustedScript")}} or string primitive, `eval()` returns the argument unchanged.
4451

4552
### Exceptions
4653

47-
Throws any exception that occurs during evaluation of the code, including {{jsxref("SyntaxError")}} if `script` fails to be parsed as a script.
54+
- {{jsxref("SyntaxError")}}
55+
- : The `script` parameter cannot be parsed as a script.
56+
- {{jsxref("TypeError")}}
57+
- : `script` is passed a string when [Trusted Types](/en-US/docs/Web/API/Trusted_Types_API) are [enforced by a CSP](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) and no default policy is defined.
58+
59+
The methods also throws any exception that occurs during evaluation of the code.
4860

4961
## Description
5062

@@ -60,7 +72,8 @@ In strict mode, declaring a variable named `eval` or re-assigning `eval` is a {{
6072
const eval = 1; // SyntaxError: Unexpected eval or arguments in strict mode
6173
```
6274

63-
If the argument of `eval()` is not a string, `eval()` returns the argument unchanged. In the following example, passing a `String` object instead of a primitive causes `eval()` to return the `String` object rather than evaluating the string.
75+
If the argument of `eval()` is not a {{domxref("TrustedScript")}} or string, `eval()` returns the argument unchanged.
76+
In the following example, passing a `String` object instead of a primitive causes `eval()` to return the `String` object rather than evaluating the string.
6477

6578
```js
6679
eval(new String("2 + 2")); // returns a String object containing "2 + 2"
@@ -345,8 +358,85 @@ Note that since JSON syntax is limited compared to JavaScript syntax, many valid
345358
346359
Passing carefully constrained data instead of arbitrary code is a good idea in general. For example, an extension designed to scrape contents of web-pages could have the scraping rules defined in [XPath](/en-US/docs/Web/XML/XPath) instead of JavaScript code.
347360
361+
### Security considerations
362+
363+
The method can be used to execute arbitrary input with the privileges of the caller.
364+
If the input is a potentially unsafe string provided by a user, this is a possible vector for [Cross-site-scripting (XSS)](/en-US/docs/Web/Security/Attacks/XSS) attacks.
365+
366+
For example, the following code shows how `eval()` might execute `untrustedCode` provided by a user:
367+
368+
```js example-bad
369+
const untrustedCode = "alert('Potentially evil code!');";
370+
const adder = eval(untrustedCode);
371+
```
372+
373+
Websites with a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) that specifies [`script-src`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src) will prevent such code running by default.
374+
You can specify [`unsafe-eval`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#unsafe-eval) in your CSP to allow `eval()` to execute, but this is unsafe as it disables one of the main protections of CSP.
375+
376+
If you must allow the scripts to run via `eval()` you can mitigate the risks by always assigning a {{domxref("TrustedScript")}} instance instead of a string, and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive.
377+
This ensures that the input is passed through a transformation function.
378+
379+
To allow `eval()` to run, you will additionally need to specify the [`trusted-types-eval` keyword](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#trusted-types-eval) in your CSP `script-src` directive.
380+
This acts in the same way as `unsafe-eval`, but _only_ allows the method to evaluate if trusted types are enabled (if you were to use `unsafe-eval` it would allow execution even on browsers that do not support trusted types).
381+
382+
For example, the required CSP for your site might look like this:
383+
384+
```http
385+
Content-Security-Policy: require-trusted-types-for 'script'; script-src '<your_allowlist>' 'trusted-types-eval'
386+
```
387+
388+
The behavior of the transformation function will depend on the specific use case that requires a user provided script.
389+
If possible you should lock the allowed scripts to exactly the code that you trust to run.
390+
If that is not possible, you might allow or block the use of certain functions within the provided input.
391+
348392
## Examples
349393
394+
Note that the first example shows how to use the method with trusted types.
395+
The other examples omit this step for brevity.
396+
397+
### Using TrustedScript
398+
399+
To mitigate the risk of XSS, we should always assign `TrustedScript` instances to the `script` parameter.
400+
We also need to do this if we're enforcing trusted types for other reasons and we want to allow some script sources that have been permitted (by `CSP: script-src`).
401+
402+
Trusted types are not yet supported on all browsers, so first we define the [trusted types tinyfill](/en-US/docs/Web/API/Trusted_Types_API#trusted_types_tinyfill).
403+
This acts as a transparent replacement for the trusted types JavaScript API:
404+
405+
```js
406+
if (typeof trustedTypes === "undefined")
407+
trustedTypes = { createPolicy: (n, rules) => rules };
408+
```
409+
410+
Next we create a {{domxref("TrustedTypePolicy")}} that defines a {{domxref("TrustedTypePolicy/createScript", "createScript()")}} method for transforming input strings into {{domxref("TrustedScript")}} instances.
411+
412+
For the purpose of this example we'll assume that we have a function `transformedScript()` that defines our tranformation/filtering logic.
413+
414+
```js
415+
const policy = trustedTypes.createPolicy("script-policy", {
416+
createScript(input) {
417+
const transformed = transformedScript(input); // Our filter method
418+
return transformed;
419+
},
420+
});
421+
```
422+
423+
Then we use the `policy` object to create a `trustedScript` object from a potentially unsafe input string:
424+
425+
```js
426+
// The potentially malicious string
427+
// We won't be including untrustedScript in our scriptAllowList array
428+
const untrustedScript = "alert('Potentially evil code!');";
429+
430+
// Create a TrustedScriptURL instance using the policy
431+
const trustedScript = policy.createScript(untrustedScript);
432+
```
433+
434+
The `trustedScriptURL` property can now be used in `eval()`
435+
436+
```js
437+
eval(trustedScriptURL);
438+
```
439+
350440
### Using eval()
351441
352442
In the following code, both of the statements containing `eval()` return 42.

files/en-us/web/javascript/reference/global_objects/function/function/index.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ Function(arg1, arg2, /* …, */ argN, functionBody)
5757
- `functionBody`
5858
- : A {{domxref("TrustedScript")}} or a string containing the JavaScript statements comprising the function definition.
5959

60+
### Exceptions
61+
62+
- {{jsxref("SyntaxError")}}
63+
- : Function parameter arguments can't be evaluated as valid identifiers, or the `functionBody` can't be parsed as a script.
64+
- {{jsxref("TypeError")}}
65+
- : Any parameter is passed a string when [Trusted Types](/en-US/docs/Web/API/Trusted_Types_API) are [enforced by a CSP](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) and no default policy is defined.
66+
67+
The methods also throws any exception that occurs during evaluation of the code.
68+
6069
## Description
6170

6271
`Function` objects created with the `Function` constructor are parsed when the function is created. This is less efficient than creating a function with a [function expression](/en-US/docs/Web/JavaScript/Reference/Operators/function) or [function declaration](/en-US/docs/Web/JavaScript/Reference/Statements/function) and calling it within your code, because such functions are parsed with the rest of the code.
@@ -109,12 +118,12 @@ const adder = new Function("a", "b", untrustedCode);
109118
```
110119

111120
Websites with a [Content Security Policy (CSP)](/en-US/docs/Web/HTTP/Guides/CSP) that specifies [`script-src`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src) will prevent such code running by default.
112-
113-
You can specify [`unsafe-eval`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#unsafe-eval) in your CSP to allow it to execute without any further restriction, but this is unsafe as it disables one of the main protections of CSP.
121+
You can specify [`unsafe-eval`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#unsafe-eval) in your CSP to allow `Function()` to execute, but this is unsafe as it disables one of the main protections of CSP.
114122

115123
If you must allow the scripts to run via `Function()` you can mitigate these issues by always assigning {{domxref("TrustedScript")}} objects instead of strings, and [enforcing trusted types](/en-US/docs/Web/API/Trusted_Types_API#using_a_csp_to_enforce_trusted_types) using the [`require-trusted-types-for`](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for) CSP directive.
116124
This ensures that the input is passed through a transformation function.
117-
Instead of specifying `unsafe-eval` you will instead use the CSP [`trusted-types-eval` keyword](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#trusted-types-eval).
125+
126+
To allow `Function()` to run, you will additionally need to specify the [`trusted-types-eval` keyword](/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#trusted-types-eval) in your CSP `script-src` directive.
118127
This acts in the same way as `unsafe-eval`, but _only_ allows the method to evaluate if trusted types are enabled (if you were to use `unsafe-eval` it would allow execution even on browsers that do not support trusted types).
119128

120129
For example, the required CSP for your site might look like this:
@@ -189,6 +198,7 @@ sayHello("world");
189198

190199
## See also
191200

201+
- [Using the function constructor](/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#using_the_function_constructor) in `eval()`
192202
- [`function`](/en-US/docs/Web/JavaScript/Reference/Statements/function)
193203
- [`function` expression](/en-US/docs/Web/JavaScript/Reference/Operators/function)
194204
- {{jsxref("Functions", "Functions", "", 1)}}

0 commit comments

Comments
 (0)