From d8a1b998c479eb2c438b8cff2232ead6eb4865c2 Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 24 Feb 2026 16:10:34 +0100 Subject: [PATCH] fix: add nonce to script-src-elem and style-src-elem when configured --- system/HTTP/ContentSecurityPolicy.php | 8 ++++ .../system/HTTP/ContentSecurityPolicyTest.php | 42 +++++++++++++++++++ user_guide_src/source/changelogs/v4.7.1.rst | 1 + 3 files changed, 51 insertions(+) diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index a7c67647823b..c94fed4c8e73 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -400,6 +400,10 @@ public function getStyleNonce(): string if ($this->styleNonce === null) { $this->styleNonce = base64_encode(random_bytes(12)); $this->addStyleSrc('nonce-' . $this->styleNonce); + + if ($this->styleSrcElem !== []) { + $this->addStyleSrcElem('nonce-' . $this->styleNonce); + } } return $this->styleNonce; @@ -413,6 +417,10 @@ public function getScriptNonce(): string if ($this->scriptNonce === null) { $this->scriptNonce = base64_encode(random_bytes(12)); $this->addScriptSrc('nonce-' . $this->scriptNonce); + + if ($this->scriptSrcElem !== []) { + $this->addScriptSrcElem('nonce-' . $this->scriptNonce); + } } return $this->scriptNonce; diff --git a/tests/system/HTTP/ContentSecurityPolicyTest.php b/tests/system/HTTP/ContentSecurityPolicyTest.php index beafa54ce164..5bced590228a 100644 --- a/tests/system/HTTP/ContentSecurityPolicyTest.php +++ b/tests/system/HTTP/ContentSecurityPolicyTest.php @@ -900,6 +900,48 @@ public function testGetStyleNonce(): void ); } + public function testGetScriptNonceAddsNonceToScriptSrcElemWhenConfigured(): void + { + $this->csp->clearDirective('script-src-elem'); + $this->csp->addScriptSrcElem('cdn.example.com'); + $nonce = $this->csp->getScriptNonce(); + $this->csp->finalize($this->response); + + $directives = $this->getCspDirectives($this->response->getHeaderLine('Content-Security-Policy')); + $this->assertContains("script-src-elem cdn.example.com 'nonce-{$nonce}'", $directives); + } + + public function testGetScriptNonceDoesNotAddNonceToScriptSrcElemWhenCleared(): void + { + $this->csp->clearDirective('script-src-elem'); + $this->csp->getScriptNonce(); + $this->csp->finalize($this->response); + + $header = $this->response->getHeaderLine('Content-Security-Policy'); + $this->assertStringNotContainsString('script-src-elem', $header); + } + + public function testGetStyleNonceAddsNonceToStyleSrcElemWhenConfigured(): void + { + $this->csp->clearDirective('style-src-elem'); + $this->csp->addStyleSrcElem('cdn.example.com'); + $nonce = $this->csp->getStyleNonce(); + $this->csp->finalize($this->response); + + $directives = $this->getCspDirectives($this->response->getHeaderLine('Content-Security-Policy')); + $this->assertContains("style-src-elem cdn.example.com 'nonce-{$nonce}'", $directives); + } + + public function testGetStyleNonceDoesNotAddNonceToStyleSrcElemWhenCleared(): void + { + $this->csp->clearDirective('style-src-elem'); + $this->csp->getStyleNonce(); + $this->csp->finalize($this->response); + + $header = $this->response->getHeaderLine('Content-Security-Policy'); + $this->assertStringNotContainsString('style-src-elem', $header); + } + #[PreserveGlobalState(false)] #[RunInSeparateProcess] public function testHeaderScriptNonceEmittedOnceGetScriptNonceCalled(): void diff --git a/user_guide_src/source/changelogs/v4.7.1.rst b/user_guide_src/source/changelogs/v4.7.1.rst index 1fa19241910f..bc4b4a8dc735 100644 --- a/user_guide_src/source/changelogs/v4.7.1.rst +++ b/user_guide_src/source/changelogs/v4.7.1.rst @@ -40,6 +40,7 @@ Bugs Fixed - **ContentSecurityPolicy:** Fixed a bug where custom CSP tags were not removed from generated HTML when CSP was disabled. The method now ensures that all custom CSP tags are removed from the generated HTML. - **ContentSecurityPolicy:** Fixed a bug where ``generateNonces()`` produces corrupted JSON responses by replacing CSP nonce placeholders with unescaped double quotes. The method now automatically JSON-escapes nonce attributes when the response Content-Type is JSON. +- **ContentSecurityPolicy:** Fixed a bug where nonces generated by ``getScriptNonce()`` and ``getStyleNonce()`` were not added to the ``script-src-elem`` and ``style-src-elem`` directives, causing nonces to be silently ignored by browsers when those directives were present. - **Database:** Fixed a bug where ``BaseConnection::callFunction()`` could double-prefix already-prefixed function names. - **Database:** Fixed a bug where ``BasePreparedQuery::prepare()`` could mis-handle SQL containing colon syntax by over-broad named-placeholder replacement. It now preserves PostgreSQL cast syntax like ``::timestamp``. - **Model:** Fixed a bug where ``BaseModel::updateBatch()`` threw an exception when ``updateOnlyChanged`` was ``true`` and the index field value did not change.