Skip to content

Commit

Permalink
Merge pull request #8220 from kenjis/feat-csp-clearDirective
Browse files Browse the repository at this point in the history
feat: add CSP clearDirective() to clear existing directive
  • Loading branch information
kenjis authored Nov 19, 2023
2 parents bf7725c + 3ac6433 commit 3846204
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 30 deletions.
77 changes: 49 additions & 28 deletions system/HTTP/ContentSecurityPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@
*/
class ContentSecurityPolicy
{
/**
* CSP directives
*
* @var array<string, string>
*/
protected array $directives = [
'base-uri' => 'baseURI',
'child-src' => 'childSrc',
'connect-src' => 'connectSrc',
'default-src' => 'defaultSrc',
'font-src' => 'fontSrc',
'form-action' => 'formAction',
'frame-ancestors' => 'frameAncestors',
'frame-src' => 'frameSrc',
'img-src' => 'imageSrc',
'media-src' => 'mediaSrc',
'object-src' => 'objectSrc',
'plugin-types' => 'pluginTypes',
'script-src' => 'scriptSrc',
'style-src' => 'styleSrc',
'manifest-src' => 'manifestSrc',
'sandbox' => 'sandbox',
'report-uri' => 'reportURI',
];

/**
* Used for security enforcement
*
Expand Down Expand Up @@ -113,37 +138,37 @@ class ContentSecurityPolicy
/**
* Used for security enforcement
*
* @var string
* @var array|string
*/
protected $reportURI;
protected $scriptSrc = [];

/**
* Used for security enforcement
*
* @var array|string
*/
protected $sandbox = [];
protected $styleSrc = [];

/**
* Used for security enforcement
*
* @var array|string
*/
protected $scriptSrc = [];
protected $manifestSrc = [];

/**
* Used for security enforcement
*
* @var array|string
*/
protected $styleSrc = [];
protected $sandbox = [];

/**
* Used for security enforcement
*
* @var array|string
* @var string|null
*/
protected $manifestSrc = [];
protected $reportURI;

/**
* Used for security enforcement
Expand Down Expand Up @@ -704,26 +729,6 @@ protected function buildHeaders(ResponseInterface $response)
$response->setHeader('Content-Security-Policy', []);
$response->setHeader('Content-Security-Policy-Report-Only', []);

$directives = [
'base-uri' => 'baseURI',
'child-src' => 'childSrc',
'connect-src' => 'connectSrc',
'default-src' => 'defaultSrc',
'font-src' => 'fontSrc',
'form-action' => 'formAction',
'frame-ancestors' => 'frameAncestors',
'frame-src' => 'frameSrc',
'img-src' => 'imageSrc',
'media-src' => 'mediaSrc',
'object-src' => 'objectSrc',
'plugin-types' => 'pluginTypes',
'script-src' => 'scriptSrc',
'style-src' => 'styleSrc',
'manifest-src' => 'manifestSrc',
'sandbox' => 'sandbox',
'report-uri' => 'reportURI',
];

// inject default base & default URIs if needed
if (empty($this->baseURI)) {
$this->baseURI = 'self';
Expand All @@ -733,7 +738,7 @@ protected function buildHeaders(ResponseInterface $response)
$this->defaultSrc = 'self';
}

foreach ($directives as $name => $property) {
foreach ($this->directives as $name => $property) {
if (! empty($this->{$property})) {
$this->addToHeader($name, $this->{$property});
}
Expand Down Expand Up @@ -814,4 +819,20 @@ protected function addToHeader(string $name, $values = null)
$this->reportOnlyHeaders[$name] = implode(' ', $reportSources);
}
}

/**
* Clear the directive.
*
* @param string $directive CSP directive
*/
public function clearDirective(string $directive): void
{
if ($directive === 'report-uris') {
$this->{$this->directives[$directive]} = null;

return;
}

$this->{$this->directives[$directive]} = [];
}
}
18 changes: 18 additions & 0 deletions tests/system/HTTP/ContentSecurityPolicyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -642,4 +642,22 @@ public function testHeaderScriptNonceEmittedOnceGetScriptNonceCalled(): void
$result = $this->getHeaderEmitted('Content-Security-Policy');
$this->assertStringContainsString("script-src 'self' 'nonce-", $result);
}

public function testClearDirective(): void
{
$this->prepare();

$this->csp->addStyleSrc('css.example.com');
$this->csp->clearDirective('style-src');

$this->csp->setReportURI('http://example.com/csp/reports');
$this->csp->clearDirective('report-uri');
$this->csp->finalize($this->response);

$header = $this->response->getHeaderLine('Content-Security-Policy');

$this->assertStringNotContainsString('style-src ', $header);
$this->assertStringNotContainsString('css.example.com', $header);
$this->assertStringNotContainsString('report-uri', $header);
}
}
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ Others
- ``FileLocatorInterface`` has been added.
- **CodeIgniter:** Added a pseudo-variable ``{memory_usage}`` to show your memory
usage in your view files, which was supported by CodeIgniter 3.
- **CSP:** Added ``ContentSecurityPolicy::clearDirective()`` method to clear
existing CSP directives. See :ref:`csp-clear-directives`.

Message Changes
***************
Expand Down
21 changes: 19 additions & 2 deletions user_guide_src/source/outgoing/csp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ call basis, by providing an optional second parameter to the adding method call.
Runtime Configuration
*********************

If your application needs to make changes at run-time, you can access the instance at ``$this->response->getCSP()`` in your controllers. The
If your application needs to make changes at run-time, you can access the instance at ``$this->response->getCSP()`` in your controllers.

The
class holds a number of methods that map pretty clearly to the appropriate header value that you need to set.
Examples are shown below, with different combinations of parameters, though all accept either a directive
name or an array of them:
Expand All @@ -76,12 +78,27 @@ name or an array of them:
The first parameter to each of the "add" methods is an appropriate string value,
or an array of them.

Report Only
===========

The ``reportOnly()`` method allows you to specify the default reporting treatment
for subsequent sources, unless over-ridden. For instance, you could specify
for subsequent sources, unless over-ridden.

For instance, you could specify
that youtube.com was allowed, and then provide several allowed but reported sources:

.. literalinclude:: csp/013.php

.. _csp-clear-directives:

Clear Directives
================

If you want to clear existing CSP directives, you can use the ``clearDirective()``
method:

.. literalinclude:: csp/014.php

**************
Inline Content
**************
Expand Down
6 changes: 6 additions & 0 deletions user_guide_src/source/outgoing/csp/014.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

// get the CSP instance
$csp = $this->response->getCSP();

$csp->clearDirective('style-src');

0 comments on commit 3846204

Please sign in to comment.