From 19380aa373054a709f2ddff74112ef9def1690de Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 8 Feb 2026 18:09:58 +0530 Subject: [PATCH 1/2] Created a method to clear nonce placeholders. --- system/HTTP/ContentSecurityPolicy.php | 5 +++++ system/HTTP/ResponseTrait.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 04d93365af25..203e867372da 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -1052,4 +1052,9 @@ public function clearDirective(string $directive): void $this->{$this->directives[$directive]} = []; } + + public function clearNoncePlaceholders(string $text): string + { + return str_replace([$this->styleNonceTag, $this->scriptNonceTag], '', $text); + } } diff --git a/system/HTTP/ResponseTrait.php b/system/HTTP/ResponseTrait.php index 211663bc7987..74b9e10c530d 100644 --- a/system/HTTP/ResponseTrait.php +++ b/system/HTTP/ResponseTrait.php @@ -370,7 +370,7 @@ public function send() if ($this->CSP->enabled()) { $this->CSP->finalize($this); } else { - $this->body = str_replace(['{csp-style-nonce}', '{csp-script-nonce}'], '', $this->body ?? ''); + $this->body = $this->CSP->clearNoncePlaceholders($this->body); } $this->sendHeaders(); From 489d2016c433772ecceef72799278c887043caad Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 8 Feb 2026 18:10:08 +0530 Subject: [PATCH 2/2] Added tests --- .../system/HTTP/ContentSecurityPolicyTest.php | 63 +++++++++++++++ tests/system/HTTP/ResponseTest.php | 76 +++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/tests/system/HTTP/ContentSecurityPolicyTest.php b/tests/system/HTTP/ContentSecurityPolicyTest.php index af9638b6a8ba..6e3c320c1eaf 100644 --- a/tests/system/HTTP/ContentSecurityPolicyTest.php +++ b/tests/system/HTTP/ContentSecurityPolicyTest.php @@ -937,4 +937,67 @@ public function testClearDirective(): void $this->assertNotContains('report-uri http://example.com/csp/reports', $directives); $this->assertNotContains('report-to default', $directives); } + + public function testClearNoncePlaceholdersWithDefaultTags(): void + { + $config = new CSPConfig(); + $csp = new ContentSecurityPolicy($config); + + $body = 'Test {csp-script-nonce} and {csp-style-nonce} here'; + $cleaned = $csp->clearNoncePlaceholders($body); + + $this->assertSame('Test and here', $cleaned); + $this->assertStringNotContainsString('{csp-script-nonce}', $cleaned); + $this->assertStringNotContainsString('{csp-style-nonce}', $cleaned); + } + + public function testClearNoncePlaceholdersWithCustomTags(): void + { + $config = new CSPConfig(); + $config->scriptNonceTag = '{custom-script-nonce}'; + $config->styleNonceTag = '{custom-style-nonce}'; + $csp = new ContentSecurityPolicy($config); + + $body = 'Test {custom-script-nonce} and {custom-style-nonce} here'; + $cleaned = $csp->clearNoncePlaceholders($body); + + $this->assertSame('Test and here', $cleaned); + $this->assertStringNotContainsString('{custom-script-nonce}', $cleaned); + $this->assertStringNotContainsString('{custom-style-nonce}', $cleaned); + } + + public function testClearNoncePlaceholdersWithEmptyBody(): void + { + $config = new CSPConfig(); + $csp = new ContentSecurityPolicy($config); + + $body = ''; + $cleaned = $csp->clearNoncePlaceholders($body); + + $this->assertSame('', $cleaned); + } + + public function testClearNoncePlaceholdersWithNoPlaceholders(): void + { + $config = new CSPConfig(); + $csp = new ContentSecurityPolicy($config); + + $body = 'Test body with no placeholders'; + $cleaned = $csp->clearNoncePlaceholders($body); + + $this->assertSame($body, $cleaned); + } + + public function testClearNoncePlaceholdersWithMultiplePlaceholders(): void + { + $config = new CSPConfig(); + $csp = new ContentSecurityPolicy($config); + + $body = ''; + $cleaned = $csp->clearNoncePlaceholders($body); + + $this->assertStringNotContainsString('{csp-script-nonce}', $cleaned); + $this->assertStringNotContainsString('{csp-style-nonce}', $cleaned); + $this->assertSame('', $cleaned); + } } diff --git a/tests/system/HTTP/ResponseTest.php b/tests/system/HTTP/ResponseTest.php index 84408df0e5a5..9ee3612da492 100644 --- a/tests/system/HTTP/ResponseTest.php +++ b/tests/system/HTTP/ResponseTest.php @@ -577,4 +577,80 @@ public function testPretendOutput(): void $this->assertSame('Happy days', $actual); } + + public function testSendRemovesDefaultNoncePlaceholdersWhenCSPDisabled(): void + { + $config = new App(); + $config->CSPEnabled = false; + + $response = new Response($config); + $response->pretend(true); + + $body = ''; + $response->setBody($body); + + ob_start(); + $response->send(); + $actual = ob_get_contents(); + ob_end_clean(); + + // Nonce placeholders should be removed when CSP is disabled + $this->assertStringNotContainsString('{csp-script-nonce}', $actual); + $this->assertStringNotContainsString('{csp-style-nonce}', $actual); + $this->assertStringContainsString('', $actual); + $this->assertStringContainsString('', $actual); + } + + public function testSendRemovesCustomNoncePlaceholdersWhenCSPDisabled(): void + { + $appConfig = new App(); + $appConfig->CSPEnabled = false; + + // Create custom CSP config with custom nonce tags + $cspConfig = new \Config\ContentSecurityPolicy(); + $cspConfig->scriptNonceTag = '{custom-script-tag}'; + $cspConfig->styleNonceTag = '{custom-style-tag}'; + + $response = new Response($appConfig); + $response->pretend(true); + + // Inject the custom CSP config + $reflection = new \ReflectionClass($response); + $cspProperty = $reflection->getProperty('CSP'); + $cspProperty->setValue($response, new ContentSecurityPolicy($cspConfig)); + + $body = ''; + $response->setBody($body); + + ob_start(); + $response->send(); + $actual = ob_get_contents(); + ob_end_clean(); + + // Custom nonce placeholders should be removed when CSP is disabled + $this->assertStringNotContainsString('{custom-script-tag}', $actual); + $this->assertStringNotContainsString('{custom-style-tag}', $actual); + $this->assertStringContainsString('', $actual); + $this->assertStringContainsString('', $actual); + } + + public function testSendWithCSPDisabledDoesNotAffectBodyWithoutNonceTags(): void + { + $config = new App(); + $config->CSPEnabled = false; + + $response = new Response($config); + $response->pretend(true); + + $body = ''; + $response->setBody($body); + + ob_start(); + $response->send(); + $actual = ob_get_contents(); + ob_end_clean(); + + // Body without nonce tags should remain unchanged + $this->assertSame($body, $actual); + } }