From 71d8b4510c14d3bd08c09f298afe35321baffbbe Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Sun, 17 Oct 2021 23:15:24 -0700 Subject: [PATCH] Add hook for WebAssembly using 'wasm-unsafe-eval' (#293) This change defines a new keyword 'wasm-unsafe-eval' for the script-src directive, and a new algorithm to gate WebAssembly compilation/instantiation behind Content Security Policy. The algorithm allows WebAssembly if one of the following is true: (i) there is no script-src nor default-src directive, (ii) the effective script-src/default-src specifies 'unsafe-eval', (iii) the effective script-src/default-src specifies 'wasm-unsafe-eval'. --- index.bs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index c82f57749c..be6ac752f5 100644 --- a/index.bs +++ b/index.bs @@ -139,6 +139,21 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/ type: grammar text: ASCII whitespace; url: ascii-whitespace text: INFRA; url: # + +spec: WebAssembly-js-api; urlPrefix: https://webassembly.github.io/spec/js-api/ + type: method + text: new WebAssembly.Module(); url: #dom-module-module + text: WebAssembly.compile(); url: #dom-webassembly-compile + text: WebAssembly.instantiate(); url: #dom-webassembly-instantiate + text: HostEnsureCanCompileWasmBytes(); url:#dom-host-ensure-can-compile-wasm-bytes + +spec: WebAssembly-web-api-api; urlPrefix: https://webassembly.github.io/spec/web-api/ + type: method + text: WebAssembly.compileStreaming(); url: #dom-webassembly-compilestreaming + text: WebAssembly.instantiateStreaming(); url: #dom-webassembly-instantiatestreaming + type: exception + text: WebAssembly.CompileError; url: #exceptiondef-compileerror +
 {
@@ -662,6 +677,7 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/
     keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
                      / "'strict-dynamic'" / "'unsafe-hashes'" /
                      / "'report-sample'" / "'unsafe-allow-redirects'"
+                     / "'wasm-unsafe-eval'"
 
     ISSUE: Bikeshed `unsafe-allow-redirects`.
 
@@ -713,7 +729,7 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/
 
   Each violation has a
   resource, which is
-  either `null`, "`inline`", "`eval`", or a {{URL}}. It represents the resource
+  either `null`, "`inline`", "`eval`", "`wasm-eval`", or a {{URL}}. It represents the resource
   which violated the policy.
 
   Each violation has a
@@ -1415,8 +1431,60 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/
   ISSUE(tc39/ecma262#938): {{HostEnsureCanCompileStrings()}} does not include the string which is
   going to be compiled as a parameter. We'll also need to update HTML to pipe that value through
   to CSP.
-
 
+

Integration with WebAssembly

+ +WebAssembly defines the {{HostEnsureCanCompileWasmBytes()}} abstract operation +which allows the host environment to block the compilation of WebAssembly +sources into executable code. This document defines an implementation of this +abstract operation which examines the relevant CSP +list to determine whether such compilation ought to be blocked. + +

+ EnsureCSPDoesNotBlockWasmByteCompilation(|callerRealm|, |calleeRealm|) +

+ +Given two realms (|callerRealm| and |calleeRealm|), +this algorithm returns normally if compilation is allowed, and throws a +{{WebAssembly.CompileError}} if not: + +1. Let |globals| be a list containing |callerRealm|'s [=Realm/global object=] and |calleeRealm|'s + [=Realm/global object=]. + +2. For each |global| in |globals|: + + 1. Let |result| be "`Allowed`". + + 2. For each |policy| in |global|'s [=global object/CSP list=]: + + 1. Let |source-list| be `null`. + + 2. If |policy| contains a [=directive=] whose [=directive/name=] is "`script-src`", then + set |source-list| to that [=directive=]'s [=directive/value=]. + + Otherwise if |policy| contains a [=directive=] whose [=directive/name=] is + "`default-src`", then set |source-list| to that directive's [=directive/value=]. + + 3. If |source-list| is non-`null`, and does not contain a [=source + expression=] which is an [=ASCII case-insensitive=] match for the + string "`'unsafe-eval'`", and does not contain a + [=source expression=] which is an [=ASCII case-insensitive=] match + for the string "`'wasm-unsafe-eval'`", then: + + 1. Let |violation| be the result of executing [[#create-violation-for-global]] on + |global|, |policy|, and "`script-src`". + + 2. Set |violation|'s [=violation/resource=] to "`wasm-eval`". + + 3. Execute [[#report-violation]] on |violation|. + + 4. If |policy|'s [=policy/disposition=] is "`enforce`", then set |result| to + "`Blocked`". + + 3. If |result| is "`Blocked`", throw a {{WebAssembly.CompileError}} exception. + + +

@@ -2654,7 +2722,7 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/ as in most situations there is no particular reason to have separate lists of permissions for inline event handlers and <{script}> elements. - The `script-src` directive governs five things: + The `script-src` directive governs six things: 1. Script requests MUST pass through [[#should-block-request]]. @@ -2681,7 +2749,23 @@ spec: INFRA; urlPrefix: https://infra.spec.whatwg.org/ and `script-src-elem` are not used when performing this check, instead `script-src` (or it's fallback directive) is always used. - 5. Navigation to `javascript:` URLs MUST pass through [[#script-src-inline]]. + 5. The following WebAssembly execution sinks are gated on the + "`wasm-unsafe-eval`" or the "`unsafe-eval`" source expressions: + + * {{new WebAssembly.Module()}} + * {{WebAssembly.compile()}} + * {{WebAssembly.compileStreaming()}} + * {{WebAssembly.instantiate()}} + * {{WebAssembly.instantiateStreaming()}} + + Note: the "`wasm-unsafe-eval`" source expression is the more specific + source expression. In particular, "`unsafe-eval`" permits both compilation + (and instantiation) of WebAssembly and, for example, the use of the "`eval`" operation in + JavaScript. The "`wasm-unsafe-eval`" source expression only permits + WebAssembly and does not affect JavaScript. + + 6. Navigation to `javascript:` URLs MUST pass through [[#should-block-inline]]. Such navigations + will only execute script if every policy allows inline script, as per #3 above.

`script-src` Pre-request check