Skip to content

Commit

Permalink
Merge pull request #241 from buenting/csp-request-matcher
Browse files Browse the repository at this point in the history
Allow to use custom request matcher
  • Loading branch information
Seldaek authored Jun 27, 2024
2 parents 4e0db88 + 2141441 commit 6e3fd83
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
### 3.x.x (xxxx-xx-xx)
* Added `csp > request_matcher` option to allow the use of a custom request matcher (`Symfony\Component\HttpFoundation\RequestMatcherInterface`)

### 3.1.0 (2023-12-03)
* Fixed overriding CSP header
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ private function addCspNode(): ArrayNodeDefinition
->canBeDisabled()
// CSP is enabled by default to ensure BC
->children()
->scalarNode('request_matcher')->defaultNull()->end()
->arrayNode('hosts')->scalarPrototype()->end()->defaultValue([])->end()
->arrayNode('content_types')->scalarPrototype()->end()->defaultValue([])->end()
->arrayNode('report_endpoint')
Expand Down
4 changes: 4 additions & 0 deletions src/DependencyInjection/NelmioSecurityExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public function load(array $configs, ContainerBuilder $container): void
$cspListenerDefinition->setArguments([$reportDefinition, $enforceDefinition, new Reference('nelmio_security.nonce_generator'), new Reference('nelmio_security.sha_computer'), (bool) $cspConfig['compat_headers'], $cspConfig['hosts'], $cspConfig['content_types']]);
$container->setParameter('nelmio_security.csp.hash_algorithm', $cspConfig['hash']['algorithm']);

if (isset($cspConfig['request_matcher'])) {
$cspListenerDefinition->setArgument(7, new Reference($cspConfig['request_matcher']));
}

$cspViolationLogFilterDefinition = $container->getDefinition('nelmio_security.csp_report.filter');

$container->setParameter('nelmio_security.csp.report_log_level', $cspConfig['report_endpoint']['log_level']);
Expand Down
14 changes: 12 additions & 2 deletions src/EventListener/ContentSecurityPolicyListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Nelmio\SecurityBundle\ContentSecurityPolicy\NonceGeneratorInterface;
use Nelmio\SecurityBundle\ContentSecurityPolicy\ShaComputerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
Expand All @@ -41,6 +42,7 @@ final class ContentSecurityPolicyListener extends AbstractContentTypeRestrictabl
private ?array $sha = null;
private NonceGeneratorInterface $nonceGenerator;
private ShaComputerInterface $shaComputer;
private ?RequestMatcherInterface $requestMatcher;

/**
* @param list<string> $hosts
Expand All @@ -53,7 +55,8 @@ public function __construct(
ShaComputerInterface $shaComputer,
bool $compatHeaders = true,
array $hosts = [],
array $contentTypes = []
array $contentTypes = [],
?RequestMatcherInterface $requestMatcher = null
) {
parent::__construct($contentTypes);
$this->report = $report;
Expand All @@ -62,6 +65,7 @@ public function __construct(
$this->hosts = $hosts;
$this->nonceGenerator = $nonceGenerator;
$this->shaComputer = $shaComputer;
$this->requestMatcher = $requestMatcher;
}

public function onKernelRequest(RequestEvent $e): void
Expand Down Expand Up @@ -149,7 +153,13 @@ public function onKernelResponse(ResponseEvent $e): void
return;
}

if (([] === $this->hosts || \in_array($e->getRequest()->getHost(), $this->hosts, true)) && $this->isContentTypeValid($response)) {
if ($this->requestMatcher) {

Check failure on line 156 in src/EventListener/ContentSecurityPolicyListener.php

View workflow job for this annotation

GitHub Actions / PHPStan

Only booleans are allowed in an if condition, Symfony\Component\HttpFoundation\RequestMatcherInterface|null given.
$match = $this->requestMatcher->matches($request);
} else {
$match = ([] === $this->hosts || \in_array($e->getRequest()->getHost(), $this->hosts, true)) && $this->isContentTypeValid($response);
}

if ($match) {
$signatures = $this->sha;
if (null !== $this->scriptNonce) {
$signatures['script-src'][] = 'nonce-'.$this->scriptNonce;
Expand Down
5 changes: 5 additions & 0 deletions src/Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,18 @@ Finally, an optional ``hosts`` key lets you configure which hostnames (e.g. ``fo
the CSP rule should be enforced on. If the list is empty (it is by default), all
hostnames will use the CSP rule.

If the `content_types` and `hosts` options don’t fit your needs, you can also configure a service implementing
`Symfony\Component\HttpFoundation\RequestMatcherInterface` as `request_matcher`. Then the `content_types` and `hosts`
options are no longer used.

.. code-block:: yaml
# config/packages/nelmio_security.yaml
nelmio_security:
csp:
enabled: true
report_logger_service: logger
request_matcher: null
hosts: []
content_types: []
enforce:
Expand Down

0 comments on commit 6e3fd83

Please sign in to comment.