Skip to content

Commit 242ca71

Browse files
committed
wip
1 parent 70424f9 commit 242ca71

4 files changed

Lines changed: 109 additions & 20 deletions

File tree

Classes/Http/CspHeaderMiddleware.php

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ class CspHeaderMiddleware implements MiddlewareInterface
4343
*/
4444
protected array $configuration;
4545

46+
/**
47+
* @Flow\InjectConfiguration(path="policies")
48+
* @var array<string, array<string, list<string>>>
49+
*/
50+
protected array $policies;
51+
4652
/**
4753
* @inheritDoc
4854
* @throws InvalidDirectiveException
@@ -73,16 +79,21 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
7379
*/
7480
private function getPolicyByCurrentContext(ServerRequestInterface $request): Policy
7581
{
76-
/*
77-
* There is no other way to know if we're in the backend here, we cannot inject
78-
* Neos\Neos\Domain\Service\ContentContext at this point as it throws an error.
79-
*/
80-
if (str_starts_with($request->getUri()->getPath(), '/neos')) {
81-
return $this->policyFactory->create(
82-
$this->nonce,
83-
$this->configuration['backend'],
84-
$this->configuration['custom-backend']
85-
);
82+
$path = $request->getUri()->getPath();
83+
84+
$backendUris = array_merge(
85+
$this->policies['backend']['matchUris'] ?? [],
86+
$this->policies['custom-backend']['matchUris'] ?? []
87+
);
88+
89+
foreach ($backendUris as $pattern) {
90+
if (preg_match('#' . $pattern . '#', $path)) {
91+
return $this->policyFactory->create(
92+
$this->nonce,
93+
$this->configuration['backend'],
94+
$this->configuration['custom-backend']
95+
);
96+
}
8697
}
8798

8899
return $this->policyFactory->create(

Configuration/Settings.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ Flowpack:
6767
'self': true
6868
'data:': true
6969
custom-backend: [ ]
70+
policies:
71+
backend:
72+
matchUris:
73+
- '^/neos'
74+
custom-backend:
75+
matchUris: []
7076

7177
Neos:
7278
Neos:

README.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
# Flowpack.ContentSecurityPolicy
22

33
<!-- TOC -->
4-
54
* [Flowpack.ContentSecurityPolicy](#flowpackcontentsecuritypolicy)
6-
* [Introduction](#introduction)
7-
* [Usage](#usage)
8-
* [Custom directives and values](#custom-directives-and-values)
9-
* [Show CSP configuration](#show-csp-configuration)
10-
* [Disable or report only](#disable-or-report-only)
11-
* [Nonce](#nonce)
12-
* [Backend](#backend)
13-
* [Thank you](#thank-you)
14-
5+
* [Introduction](#introduction)
6+
* [Usage](#usage)
7+
* [Deprecated Configuration](#deprecated-configuration)
8+
* [Custom directives and values](#custom-directives-and-values)
9+
* [Show CSP configuration](#show-csp-configuration)
10+
* [Disable or report only](#disable-or-report-only)
11+
* [Nonce](#nonce)
12+
* [Backend](#backend)
13+
* [Custom backend routes](#custom-backend-routes)
14+
* [Thank you](#thank-you)
1515
<!-- TOC -->
1616

1717
## Introduction
@@ -190,6 +190,24 @@ Unsafe inline scripts and styles are allowed in the backend because otherwise th
190190

191191
Again you can add your own policies in the custom-backend array the same way as the custom array for the frontend.
192192

193+
### Custom backend routes
194+
195+
By default, the backend policy is applied to all paths starting with `/neos`. If you have additional routes that require
196+
the same permissive policy (e.g. a custom admin UI at `/monocle`), add them to `custom-backend.matchUris`. Each entry
197+
is a PHP regex (without delimiters) matched against the request path.
198+
199+
```yaml
200+
Flowpack:
201+
ContentSecurityPolicy:
202+
policies:
203+
custom-backend:
204+
matchUris:
205+
- '^/monocle(/.*)?$'
206+
```
207+
208+
The built-in `'^/neos'` pattern in `backend.matchUris` is unaffected, so the Neos backend continues to work without any
209+
changes. You only need to touch `backend.matchUris` if you want to replace the default `/neos` match entirely.
210+
193211
## Thank you
194212

195213
This package originates from https://github.com/LarsNieuwenhuizen/Nieuwenhuizen.ContentSecurityPolicy.

Tests/Unit/Http/CspHeaderMiddlewareTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ protected function setUp(): void
6969
['backend' => [], 'custom-backend' => [], 'default' => [], 'custom' => [],]
7070
);
7171

72+
$reflectionProperty = $this->middlewareReflection->getProperty('policies');
73+
$reflectionProperty->setValue(
74+
$this->middleware,
75+
['backend' => ['matchUris' => ['^/neos']], 'custom-backend' => ['matchUris' => []]]
76+
);
77+
7278
$this->requestHandlerMock->expects($this->once())->method('handle')->willReturn($this->responseMock);
7379
}
7480

@@ -112,4 +118,52 @@ public function testProcessShouldAddHeadersToResponseAndReplaceBody(): void
112118

113119
$this->middleware->process($this->requestMock, $this->requestHandlerMock);
114120
}
121+
122+
public function testProcessShouldUseBackendPolicyForCustomMatchUri(): void
123+
{
124+
$reflectionProperty = $this->middlewareReflection->getProperty('policies');
125+
$reflectionProperty->setValue(
126+
$this->middleware,
127+
['backend' => ['matchUris' => ['^/neos']], 'custom-backend' => ['matchUris' => ['^/monocle(/.*)?$']]]
128+
);
129+
130+
$this->requestMock->expects($this->once())->method('getUri')->willReturn($this->uriMock);
131+
$this->uriMock->expects($this->once())->method('getPath')->willReturn('/monocle/dashboard');
132+
133+
$this->policyFactoryMock->expects($this->once())->method('create')->willReturn($this->policyMock);
134+
$this->policyMock->expects($this->once())->method('hasNonceDirectiveValue')->willReturn(false);
135+
$this->responseMock->expects($this->once())->method('withAddedHeader')->willReturnSelf();
136+
137+
$this->middleware->process($this->requestMock, $this->requestHandlerMock);
138+
}
139+
140+
public function testProcessShouldUseDefaultPolicyWhenNoMatchUriMatches(): void
141+
{
142+
$this->requestMock->expects($this->once())->method('getUri')->willReturn($this->uriMock);
143+
$this->uriMock->expects($this->once())->method('getPath')->willReturn('/monocle/dashboard');
144+
145+
$this->policyFactoryMock->expects($this->once())->method('create')->willReturn($this->policyMock);
146+
$this->policyMock->expects($this->once())->method('hasNonceDirectiveValue')->willReturn(false);
147+
$this->responseMock->expects($this->once())->method('withAddedHeader')->willReturnSelf();
148+
149+
$this->middleware->process($this->requestMock, $this->requestHandlerMock);
150+
}
151+
152+
public function testProcessShouldNotMatchNeosWhenBackendMatchUrisOverridden(): void
153+
{
154+
$reflectionProperty = $this->middlewareReflection->getProperty('policies');
155+
$reflectionProperty->setValue(
156+
$this->middleware,
157+
['backend' => ['matchUris' => ['^/other']], 'custom-backend' => ['matchUris' => []]]
158+
);
159+
160+
$this->requestMock->expects($this->once())->method('getUri')->willReturn($this->uriMock);
161+
$this->uriMock->expects($this->once())->method('getPath')->willReturn('/neos');
162+
163+
$this->policyFactoryMock->expects($this->once())->method('create')->willReturn($this->policyMock);
164+
$this->policyMock->expects($this->once())->method('hasNonceDirectiveValue')->willReturn(false);
165+
$this->responseMock->expects($this->once())->method('withAddedHeader')->willReturnSelf();
166+
167+
$this->middleware->process($this->requestMock, $this->requestHandlerMock);
168+
}
115169
}

0 commit comments

Comments
 (0)