Skip to content

Commit fa5ad9d

Browse files
committed
Update CSP Negotiation
1 parent 07e070e commit fa5ad9d

2 files changed

Lines changed: 165 additions & 11 deletions

File tree

src/Response.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -568,21 +568,29 @@ public function isReplacingHeaders() : bool
568568

569569
/**
570570
* Set the Content-Security-Policy and Content-Security-Policy-Report-Only
571-
* headers if the CSP classes are set and the response has not downloads.
571+
* headers if the CSP classes are set and the response has an empty body,
572+
* has not downloads and the Content-Type is not text/html.
572573
*
573574
* @return void
574575
*/
575576
protected function negotiateCsp() : void
576577
{
578+
if ($this->getBody() === '' || $this->hasDownload()) {
579+
return;
580+
}
581+
$contentType = (string) $this->getHeader(ResponseHeader::CONTENT_TYPE);
582+
if (!\str_contains($contentType, 'text/html')) {
583+
return;
584+
}
577585
$csp = $this->getCsp();
578-
if ($csp && !$this->hasDownload()) {
586+
if ($csp) {
579587
$this->setHeader(
580588
ResponseHeader::CONTENT_SECURITY_POLICY,
581589
$csp->render()
582590
);
583591
}
584592
$csp = $this->getCspReportOnly();
585-
if ($csp && !$this->hasDownload()) {
593+
if ($csp) {
586594
$this->setHeader(
587595
ResponseHeader::CONTENT_SECURITY_POLICY_REPORT_ONLY,
588596
$csp->render()

tests/ResponseTest.php

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -695,55 +695,201 @@ public function testSetContentEncoding() : void
695695
*/
696696
public function testCsp() : void
697697
{
698-
self::assertNull($this->response->getCsp());
699-
self::assertFalse($this->response->hasCsp());
700698
$csp = new CSP([
701699
CSP::defaultSrc => [
702700
'self',
703701
],
704702
]);
705703
$this->response->setCsp($csp);
706-
self::assertSame($csp, $this->response->getCsp());
707-
self::assertTrue($this->response->hasCsp());
704+
$this->response->setBody('Hello!');
708705
\ob_start();
709706
$this->response->send();
710707
\ob_get_clean();
711708
self::assertContains(
712709
"Content-Security-Policy: default-src 'self';",
713710
xdebug_get_headers()
714711
);
712+
}
713+
714+
/**
715+
* @runInSeparateProcess
716+
*/
717+
public function testCspNotSet() : void
718+
{
719+
$this->response->setBody('Hello!');
720+
\ob_start();
721+
$this->response->send();
722+
\ob_get_clean();
723+
self::assertNotContains(
724+
"Content-Security-Policy: default-src 'self';",
725+
xdebug_get_headers()
726+
);
727+
}
728+
729+
/**
730+
* @runInSeparateProcess
731+
*/
732+
public function testCspSettersAndGetters() : void
733+
{
734+
self::assertNull($this->response->getCsp());
735+
self::assertFalse($this->response->hasCsp());
736+
$csp = new CSP([
737+
CSP::defaultSrc => [
738+
'self',
739+
],
740+
]);
741+
$this->response->setCsp($csp);
742+
self::assertSame($csp, $this->response->getCsp());
743+
self::assertTrue($this->response->hasCsp());
715744
$this->response->removeCsp();
716745
self::assertNull($this->response->getCsp());
717746
self::assertFalse($this->response->hasCsp());
718747
}
719748

749+
/**
750+
* @runInSeparateProcess
751+
*/
752+
public function testCspNegotiationWithoutBody() : void
753+
{
754+
$csp = new CSP([
755+
CSP::defaultSrc => [
756+
'self',
757+
],
758+
]);
759+
$this->response->setCsp($csp);
760+
$this->response->setBody('');
761+
\ob_start();
762+
$this->response->send();
763+
\ob_get_clean();
764+
self::assertNotContains(
765+
"Content-Security-Policy: default-src 'self';",
766+
xdebug_get_headers()
767+
);
768+
}
769+
770+
/**
771+
* @runInSeparateProcess
772+
*/
773+
public function testCspNegotiationWithInvalidContentType() : void
774+
{
775+
$csp = new CSP([
776+
CSP::defaultSrc => [
777+
'self',
778+
],
779+
]);
780+
$this->response->setCsp($csp);
781+
$this->response->setJson([
782+
'name' => 'John Doe',
783+
]);
784+
\ob_start();
785+
$this->response->send();
786+
\ob_get_clean();
787+
self::assertNotContains(
788+
"Content-Security-Policy: default-src 'self';",
789+
xdebug_get_headers()
790+
);
791+
}
792+
720793
/**
721794
* @runInSeparateProcess
722795
*/
723796
public function testCspReportOnly() : void
724797
{
725-
self::assertNull($this->response->getCspReportOnly());
726-
self::assertFalse($this->response->hasCspReportOnly());
727798
$csp = new CSP([
728799
CSP::defaultSrc => [
729800
'self',
730801
],
731802
]);
732803
$this->response->setCspReportOnly($csp);
733-
self::assertSame($csp, $this->response->getCspReportOnly());
734-
self::assertTrue($this->response->hasCspReportOnly());
804+
$this->response->setBody('Hello!');
735805
\ob_start();
736806
$this->response->send();
737807
\ob_get_clean();
738808
self::assertContains(
739809
"Content-Security-Policy-Report-Only: default-src 'self';",
740810
xdebug_get_headers()
741811
);
812+
}
813+
814+
/**
815+
* @runInSeparateProcess
816+
*/
817+
public function testCspReportOnlyNotSet() : void
818+
{
819+
$this->response->setBody('Hello!');
820+
\ob_start();
821+
$this->response->send();
822+
\ob_get_clean();
823+
self::assertNotContains(
824+
"Content-Security-Policy-Report-Only: default-src 'self';",
825+
xdebug_get_headers()
826+
);
827+
}
828+
829+
/**
830+
* @runInSeparateProcess
831+
*/
832+
public function testCspReportOnlySettersAndGetters() : void
833+
{
834+
self::assertNull($this->response->getCspReportOnly());
835+
self::assertFalse($this->response->hasCspReportOnly());
836+
$csp = new CSP([
837+
CSP::defaultSrc => [
838+
'self',
839+
],
840+
]);
841+
$this->response->setCspReportOnly($csp);
842+
self::assertSame($csp, $this->response->getCspReportOnly());
843+
self::assertTrue($this->response->hasCspReportOnly());
742844
$this->response->removeCspReportOnly();
743845
self::assertNull($this->response->getCspReportOnly());
744846
self::assertFalse($this->response->hasCspReportOnly());
745847
}
746848

849+
/**
850+
* @runInSeparateProcess
851+
*/
852+
public function testCspReportOnlyNegotiationWithoutBody() : void
853+
{
854+
$csp = new CSP([
855+
CSP::defaultSrc => [
856+
'self',
857+
],
858+
]);
859+
$this->response->setCspReportOnly($csp);
860+
$this->response->setBody('');
861+
\ob_start();
862+
$this->response->send();
863+
\ob_get_clean();
864+
self::assertNotContains(
865+
"Content-Security-Policy-Report-Only: default-src 'self';",
866+
xdebug_get_headers()
867+
);
868+
}
869+
870+
/**
871+
* @runInSeparateProcess
872+
*/
873+
public function testCspReportOnlyNegotiationWithInvalidContentType() : void
874+
{
875+
$csp = new CSP([
876+
CSP::defaultSrc => [
877+
'self',
878+
],
879+
]);
880+
$this->response->setCspReportOnly($csp);
881+
$this->response->setJson([
882+
'name' => 'John Doe',
883+
]);
884+
\ob_start();
885+
$this->response->send();
886+
\ob_get_clean();
887+
self::assertNotContains(
888+
"Content-Security-Policy-Report-Only: default-src 'self';",
889+
xdebug_get_headers()
890+
);
891+
}
892+
747893
/**
748894
* @runInSeparateProcess
749895
*/

0 commit comments

Comments
 (0)