diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index dfea52a62772..e2a6bafa43c0 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -524,13 +524,32 @@ public function addConnectSrc($uri, ?bool $explicitReporting = null) * * @return $this */ - public function setDefaultSrc($uri, ?bool $explicitReporting = null) + public function addDefaultSrc($uri, ?bool $explicitReporting = null) { - $this->defaultSrc = [(string) $uri => $explicitReporting ?? $this->reportOnly]; + $this->addOption($uri, 'defaultSrc', $explicitReporting ?? $this->reportOnly); return $this; } + /** + * Sets the `default-src` directive, replacing any existing sources. + * + * `default-src` is the URI that is used for many of the settings when + * no other source has been set. + * + * @see http://www.w3.org/TR/CSP/#directive-default-src + * + * @param list|string $uri + * + * @return $this + */ + public function setDefaultSrc($uri, ?bool $explicitReporting = null) + { + $this->defaultSrc = []; + + return $this->addDefaultSrc($uri, $explicitReporting); + } + /** * Adds a new value to the `font-src` directive. * diff --git a/tests/system/HTTP/ContentSecurityPolicyTest.php b/tests/system/HTTP/ContentSecurityPolicyTest.php index 780cb99f31ea..a85cd107e7c3 100644 --- a/tests/system/HTTP/ContentSecurityPolicyTest.php +++ b/tests/system/HTTP/ContentSecurityPolicyTest.php @@ -617,6 +617,50 @@ public function testDefaultSrc(): void $this->assertContains('default-src iffy.com', $this->getCspDirectives($header)); } + #[PreserveGlobalState(false)] + #[RunInSeparateProcess] + public function testDefaultSrcRich(): void + { + $this->csp->reportOnly(false); + $this->csp->setDefaultSrc(['self', 'maybe.com', 'iffy.com']); + $this->assertTrue($this->work()); + + $header = $this->getHeaderEmitted('Content-Security-Policy'); + $this->assertIsString($header); + $this->assertContains("default-src 'self' maybe.com iffy.com", $this->getCspDirectives($header)); + } + + #[PreserveGlobalState(false)] + #[RunInSeparateProcess] + public function testAddDefaultSrc(): void + { + $this->csp->addDefaultSrc('maybe.com'); + $this->csp->addDefaultSrc('iffy.com', true); + $this->assertTrue($this->work()); + + $header = $this->getHeaderEmitted('Content-Security-Policy-Report-Only'); + $this->assertIsString($header); + $this->assertContains('default-src iffy.com', $this->getCspDirectives($header)); + + $header = $this->getHeaderEmitted('Content-Security-Policy'); + $this->assertIsString($header); + $this->assertContains('default-src maybe.com', $this->getCspDirectives($header)); + } + + #[PreserveGlobalState(false)] + #[RunInSeparateProcess] + public function testAddDefaultSrcAppends(): void + { + $this->csp->reportOnly(false); + $this->csp->setDefaultSrc('maybe.com'); + $this->csp->addDefaultSrc('iffy.com'); + $this->assertTrue($this->work()); + + $header = $this->getHeaderEmitted('Content-Security-Policy'); + $this->assertIsString($header); + $this->assertContains('default-src maybe.com iffy.com', $this->getCspDirectives($header)); + } + #[PreserveGlobalState(false)] #[RunInSeparateProcess] public function testReportURI(): void diff --git a/user_guide_src/source/changelogs/v4.7.6.rst b/user_guide_src/source/changelogs/v4.7.6.rst index 8739e9546d4c..e210d7897c06 100644 --- a/user_guide_src/source/changelogs/v4.7.6.rst +++ b/user_guide_src/source/changelogs/v4.7.6.rst @@ -38,6 +38,7 @@ Bugs Fixed is used instead. Values that cannot be converted to a string, such as objects not implementing ``Stringable``, now throw a ``FormatException`` instead of a fatal ``Error``. +- **HTTP:** Fixed a bug in ``ContentSecurityPolicy::setDefaultSrc()`` where passing an array of URIs resulted in an invalid ``'Array'`` directive. Added ``ContentSecurityPolicy::addDefaultSrc()`` to allow appending sources to the ``default-src`` directive. - **Security:** Fixed a bug where a valid JSON request body that is not an object (for example a JSON array) was corrupted after CSRF verification when the token was sent via the ``X-CSRF-TOKEN`` header.