]> source.dussan.org Git - nextcloud-server.git/commitdiff
Allow "wasm-unsafe-eval" in CSP 38082/head
authorDaniel Calviño Sánchez <danxuliu@gmail.com>
Thu, 4 May 2023 14:54:23 +0000 (16:54 +0200)
committerDaniel Calviño Sánchez <danxuliu@gmail.com>
Thu, 10 Aug 2023 00:38:41 +0000 (02:38 +0200)
If a page has a Content Security Policy header and the `script-src` (or
`default-src`) directive does not contain neither `wasm-unsafe-eval` nor
`unsafe-eval` loading and executing WebAssembly is blocked in the page
(although it is still possible to load and execute WebAssembly in a
worker thread).

Although the Nextcloud classes to manage the CSP already supported
allowing `unsafe-eval` this affects not only WebAssembly, but also the
`eval` operation in JavaScript.

To make possible to allow WebAssembly execution without allowing
JavaScript `eval` this commit adds support for allowing
`wasm-unsafe-eval`.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
lib/private/Security/CSP/ContentSecurityPolicy.php
lib/public/AppFramework/Http/ContentSecurityPolicy.php
lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
lib/public/AppFramework/Http/StrictContentSecurityPolicy.php
tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php
tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php

index 8d9551c8978154e34c26415ad23c2110c49cb1b8..e2d115cf34ec61ea226233d5111c076313fbfa0b 100644 (file)
@@ -64,6 +64,14 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy
                $this->evalScriptAllowed = $evalScriptAllowed;
        }
 
+       public function isEvalWasmAllowed(): ?bool {
+               return $this->evalWasmAllowed;
+       }
+
+       public function setEvalWasmAllowed(bool $evalWasmAllowed): void {
+               $this->evalWasmAllowed = $evalWasmAllowed;
+       }
+
        /**
         * @return array
         */
index 0e3a6a705d5ffabc5f812e392aa5c17850a5770b..f17dd9bd270480136f318f3be0f4df5412b25c0a 100644 (file)
@@ -44,6 +44,8 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
        protected $inlineScriptAllowed = false;
        /** @var bool Whether eval in JS scripts is allowed */
        protected $evalScriptAllowed = false;
+       /** @var bool Whether WebAssembly compilation is allowed */
+       protected ?bool $evalWasmAllowed = false;
        /** @var bool Whether strict-dynamic should be set */
        protected $strictDynamicAllowed = false;
        /** @var array Domains from which scripts can get loaded */
index 035b4f01f60d44dfa25332c50313ced530a0acf0..7e1de2ef2eb93e271a7549c411a3500c871bd8f4 100644 (file)
@@ -47,6 +47,8 @@ class EmptyContentSecurityPolicy {
         * @link https://github.com/owncloud/core/issues/11925
         */
        protected $evalScriptAllowed = null;
+       /** @var bool Whether WebAssembly compilation is allowed */
+       protected ?bool $evalWasmAllowed = null;
        /** @var array Domains from which scripts can get loaded */
        protected $allowedScriptDomains = null;
        /**
@@ -116,6 +118,17 @@ class EmptyContentSecurityPolicy {
                return $this;
        }
 
+       /**
+        * Whether WebAssembly compilation is allowed or forbidden
+        * @param bool $state
+        * @return $this
+        * @since 28.0.0
+        */
+       public function allowEvalWasm(bool $state = true) {
+               $this->evalWasmAllowed = $state;
+               return $this;
+       }
+
        /**
         * Allows to execute JavaScript files from a specific domain. Use * to
         * allow JavaScript from all domains.
@@ -433,7 +446,7 @@ class EmptyContentSecurityPolicy {
                $policy .= "base-uri 'none';";
                $policy .= "manifest-src 'self';";
 
-               if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed) {
+               if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed || $this->evalWasmAllowed) {
                        $policy .= 'script-src ';
                        if (is_string($this->useJsNonce)) {
                                if ($this->strictDynamicAllowed) {
@@ -453,6 +466,9 @@ class EmptyContentSecurityPolicy {
                        if ($this->evalScriptAllowed) {
                                $policy .= ' \'unsafe-eval\'';
                        }
+                       if ($this->evalWasmAllowed) {
+                               $policy .= ' \'wasm-unsafe-eval\'';
+                       }
                        $policy .= ';';
                }
 
index ed137bad930584ca14c1b2b1bf37708d8c4658b7..96c03673d5ca23d7cde1bebcecf7ecdbd70a4a43 100644 (file)
@@ -46,6 +46,8 @@ class StrictContentSecurityPolicy extends EmptyContentSecurityPolicy {
        protected $inlineScriptAllowed = false;
        /** @var bool Whether eval in JS scripts is allowed */
        protected $evalScriptAllowed = false;
+       /** @var bool Whether WebAssembly compilation is allowed */
+       protected ?bool $evalWasmAllowed = false;
        /** @var array Domains from which scripts can get loaded */
        protected $allowedScriptDomains = [
                '\'self\'',
index 53632da93d156930246515f65d47580c59a9226f..8e6ac32b416cd1eb5d35afecc1e45388d00e9494 100644 (file)
@@ -456,6 +456,13 @@ class ContentSecurityPolicyTest extends \Test\TestCase {
                $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
        }
 
+       public function testGetPolicyUnsafeWasmEval() {
+               $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'wasm-unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
+
+               $this->contentSecurityPolicy->allowEvalWasm(true);
+               $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+       }
+
        public function testGetPolicyNonce() {
                $nonce = 'my-nonce';
                $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
index dc10d095ab5363539c4a1e700759a1457ff90ab6..328e464f981cfce0faf4bea85b9f4765c6cfa750 100644 (file)
@@ -75,6 +75,13 @@ class EmptyContentSecurityPolicyTest extends \Test\TestCase {
                $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
        }
 
+       public function testGetPolicyScriptAllowWasmEval() {
+               $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src  'wasm-unsafe-eval';frame-ancestors 'none'";
+
+               $this->contentSecurityPolicy->allowEvalWasm(true);
+               $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+       }
+
        public function testGetPolicyStyleDomainValid() {
                $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.owncloud.com;frame-ancestors 'none'";