44
55namespace Unit \Http ;
66
7+ use Exception ;
78use Flowpack \ContentSecurityPolicy \Factory \PolicyFactory ;
89use Flowpack \ContentSecurityPolicy \Helpers \TagHelper ;
910use Flowpack \ContentSecurityPolicy \Http \CspHeaderMiddleware ;
1718use Psr \Http \Message \ServerRequestInterface ;
1819use Psr \Http \Message \UriInterface ;
1920use Psr \Http \Server \RequestHandlerInterface ;
21+ use Psr \Log \LoggerInterface ;
2022use ReflectionClass ;
2123use Throwable ;
22-
2324use function PHPUnit \Framework \once ;
2425
2526#[CoversClass(CspHeaderMiddleware::class)]
@@ -34,6 +35,7 @@ class CspHeaderMiddlewareTest extends TestCase
3435 private readonly UriInterface &MockObject $ uriMock ;
3536 private readonly PolicyFactory &MockObject $ policyFactoryMock ;
3637 private readonly Policy &MockObject $ policyMock ;
38+ private readonly LoggerInterface &MockObject $ loggerMock ;
3739
3840 /**
3941 * @throws Throwable
@@ -51,6 +53,7 @@ protected function setUp(): void
5153 $ this ->uriMock = $ this ->createMock (UriInterface::class);
5254 $ this ->policyFactoryMock = $ this ->createMock (PolicyFactory::class);
5355 $ this ->policyMock = $ this ->createMock (Policy::class);
56+ $ this ->loggerMock = $ this ->createMock (LoggerInterface::class);
5457
5558 $ this ->middlewareReflection = new ReflectionClass ($ this ->middleware );
5659
@@ -69,6 +72,18 @@ protected function setUp(): void
6972 ['backend ' => [], 'custom-backend ' => [], 'default ' => [], 'custom ' => [],]
7073 );
7174
75+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('policies ' );
76+ $ reflectionProperty ->setValue (
77+ $ this ->middleware ,
78+ ['backend ' => ['matchUris ' => ['^/neos ' ]], 'custom-backend ' => ['matchUris ' => []]]
79+ );
80+
81+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('throwInvalidDirectiveException ' );
82+ $ reflectionProperty ->setValue ($ this ->middleware , true );
83+
84+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('logger ' );
85+ $ reflectionProperty ->setValue ($ this ->middleware , $ this ->loggerMock );
86+
7287 $ this ->requestHandlerMock ->expects ($ this ->once ())->method ('handle ' )->willReturn ($ this ->responseMock );
7388 }
7489
@@ -112,4 +127,113 @@ public function testProcessShouldAddHeadersToResponseAndReplaceBody(): void
112127
113128 $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
114129 }
130+
131+ public function testProcessShouldUseBackendPolicyForCustomMatchUri (): void
132+ {
133+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('policies ' );
134+ $ reflectionProperty ->setValue (
135+ $ this ->middleware ,
136+ ['backend ' => ['matchUris ' => ['^/neos ' ]], 'custom-backend ' => ['matchUris ' => ['^/monocle(/.*)?$ ' ]]]
137+ );
138+
139+ $ this ->requestMock ->expects ($ this ->once ())->method ('getUri ' )->willReturn ($ this ->uriMock );
140+ $ this ->uriMock ->expects ($ this ->once ())->method ('getPath ' )->willReturn ('/monocle/dashboard ' );
141+
142+ $ this ->policyFactoryMock ->expects ($ this ->once ())->method ('create ' )->willReturn ($ this ->policyMock );
143+ $ this ->policyMock ->expects ($ this ->once ())->method ('hasNonceDirectiveValue ' )->willReturn (false );
144+ $ this ->responseMock ->expects ($ this ->once ())->method ('withAddedHeader ' )->willReturnSelf ();
145+
146+ $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
147+ }
148+
149+ public function testProcessShouldUseDefaultPolicyWhenNoMatchUriMatches (): void
150+ {
151+ $ this ->requestMock ->expects ($ this ->once ())->method ('getUri ' )->willReturn ($ this ->uriMock );
152+ $ this ->uriMock ->expects ($ this ->once ())->method ('getPath ' )->willReturn ('/monocle/dashboard ' );
153+
154+ $ this ->policyFactoryMock ->expects ($ this ->once ())->method ('create ' )->willReturn ($ this ->policyMock );
155+ $ this ->policyMock ->expects ($ this ->once ())->method ('hasNonceDirectiveValue ' )->willReturn (false );
156+ $ this ->responseMock ->expects ($ this ->once ())->method ('withAddedHeader ' )->willReturnSelf ();
157+
158+ $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
159+ }
160+
161+ public function testProcessShouldNotMatchNeosWhenBackendMatchUrisOverridden (): void
162+ {
163+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('policies ' );
164+ $ reflectionProperty ->setValue (
165+ $ this ->middleware ,
166+ ['backend ' => ['matchUris ' => ['^/other ' ]], 'custom-backend ' => ['matchUris ' => []]]
167+ );
168+
169+ $ this ->requestMock ->expects ($ this ->once ())->method ('getUri ' )->willReturn ($ this ->uriMock );
170+ $ this ->uriMock ->expects ($ this ->once ())->method ('getPath ' )->willReturn ('/neos ' );
171+
172+ $ this ->policyFactoryMock ->expects ($ this ->once ())->method ('create ' )->willReturn ($ this ->policyMock );
173+ $ this ->policyMock ->expects ($ this ->once ())->method ('hasNonceDirectiveValue ' )->willReturn (false );
174+ $ this ->responseMock ->expects ($ this ->once ())->method ('withAddedHeader ' )->willReturnSelf ();
175+
176+ $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
177+ }
178+
179+ public function testProcessThrowsOnInvalidMatchUriPattern (): void
180+ {
181+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('policies ' );
182+ $ reflectionProperty ->setValue (
183+ $ this ->middleware ,
184+ ['backend ' => ['matchUris ' => ['^/neos( ' ]], 'custom-backend ' => ['matchUris ' => []]]
185+ );
186+
187+ $ this ->requestMock ->expects ($ this ->once ())->method ('getUri ' )->willReturn ($ this ->uriMock );
188+ $ this ->uriMock ->expects ($ this ->once ())->method ('getPath ' )->willReturn ('/neos ' );
189+
190+ /*
191+ * preg_match emmits a warning which makes phpunit fail, so we convert warnings to errors and expect an exception
192+ * as we cannot expect warnings
193+ */
194+ set_error_handler (static function (int $ errorCode , string $ errorString ): never {
195+ throw new Exception ($ errorString , $ errorCode );
196+ }, E_WARNING );
197+ $ this ->expectExceptionMessage ('Compilation failed ' );
198+
199+ try {
200+ $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
201+ } finally {
202+ restore_error_handler ();
203+ }
204+ }
205+
206+ public function testProcessLogsInvalidMatchUriPatternInProduction (): void
207+ {
208+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('throwInvalidDirectiveException ' );
209+ $ reflectionProperty ->setValue ($ this ->middleware , false );
210+
211+ $ reflectionProperty = $ this ->middlewareReflection ->getProperty ('policies ' );
212+ $ reflectionProperty ->setValue (
213+ $ this ->middleware ,
214+ ['backend ' => ['matchUris ' => ['^/neos( ' ]], 'custom-backend ' => ['matchUris ' => []]]
215+ );
216+
217+ $ this ->requestMock ->expects ($ this ->once ())->method ('getUri ' )->willReturn ($ this ->uriMock );
218+ $ this ->uriMock ->expects ($ this ->once ())->method ('getPath ' )->willReturn ('/neos ' );
219+
220+ $ this ->loggerMock ->expects ($ this ->once ())->method ('critical ' );
221+ $ this ->policyFactoryMock ->expects ($ this ->once ())->method ('create ' )->willReturn ($ this ->policyMock );
222+ $ this ->policyMock ->expects ($ this ->once ())->method ('hasNonceDirectiveValue ' )->willReturn (false );
223+ $ this ->responseMock ->expects ($ this ->once ())->method ('withAddedHeader ' )->willReturnSelf ();
224+
225+ /*
226+ * preg_match emmits a warning which makes phpunit fail, so we suppress the warning that would make phpunit
227+ * fail
228+ */
229+ set_error_handler (static function (): bool {
230+ return true ;
231+ }, E_WARNING );
232+
233+ try {
234+ $ this ->middleware ->process ($ this ->requestMock , $ this ->requestHandlerMock );
235+ } finally {
236+ restore_error_handler ();
237+ }
238+ }
115239}
0 commit comments