‪TYPO3CMS  ‪main
ReferrerEnforcerTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use Psr\Http\Message\ServerRequestInterface;
29 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
30 
31 final class ‪ReferrerEnforcerTest extends UnitTestCase
32 {
33  private static function ‪buildRefreshContentPattern(string $uri): string
34  {
35  return sprintf(
36  '#.+href="%s\d+" id="referrer-refresh".+#',
37  preg_quote(
38  htmlspecialchars($uri . (str_contains($uri, '?') ? '&' : '?') . 'referrer-refresh='),
39  '#'
40  )
41  );
42  }
43 
44  public static function ‪validReferrerIsHandledDataProvider(): array
45  {
46  return [
47  // Without query parameters
48  [
49  'https://example.org/typo3/login', // requestUri
50  'https://example.org/typo3/index.php', // referrer
51  null, // options
52  null, // response
53  ],
54  [
55  'https://example.org/typo3/login',
56  '',
57  ['flags' => ['refresh-empty']],
59  'https://example.org/typo3/login'
60  ),
61  ],
62  [
63  'https://example.org/typo3/login',
64  'https://example.org/?eID=handler',
65  ['flags' => ['refresh-same-site']],
67  'https://example.org/typo3/login'
68  ),
69  ],
70  [
71  'https://example.org/typo3/login',
72  'https://other-example.site/security/',
73  ['flags' => ['refresh-always']],
75  'https://example.org/typo3/login'
76  ),
77  ],
78  // With query parameters
79  [
80  'https://example.org/typo3/login?query=parameter',
81  'https://example.org/typo3/index.php',
82  null,
83  null,
84  ],
85  [
86  'https://example.org/typo3/login?query=parameter',
87  '',
88  ['flags' => ['refresh-empty']],
90  'https://example.org/typo3/login?query=parameter'
91  ),
92  ],
93  [
94  'https://example.org/typo3/login?query=parameter',
95  'https://example.org/?eID=handler',
96  ['flags' => ['refresh-same-site']],
98  'https://example.org/typo3/login?query=parameter'
99  ),
100  ],
101  [
102  'https://example.org/typo3/login?query=parameter',
103  'https://other-example.site/security/',
104  ['flags' => ['refresh-always']],
106  'https://example.org/typo3/login?query=parameter'
107  ),
108  ],
109  ];
110  }
111 
115  #[DataProvider('validReferrerIsHandledDataProvider')]
116  #[Test]
117  public function ‪validReferrerIsHandled(string $requestUri, string $referrer, ?array $options, ?string $expectedResponse): void
118  {
119  $subject = $this->‪buildSubject($requestUri, $referrer);
120  $response = $subject->handle($options);
121 
122  if ($expectedResponse === null) {
123  self::assertNull($response);
124  } else {
125  self::assertMatchesRegularExpression($expectedResponse, (string)$response->getBody());
126  }
127  }
128 
129  public static function ‪invalidReferrerIsHandledDataProvider(): array
130  {
131  return [
132  [
133  'https://example.org/typo3/login', // requestUri
134  'https://example.org/?eID=handler', // referrer
135  null, // options
136  ],
137  [
138  'https://example.org/typo3/login',
139  'https://example.org/?eID=handler',
140  ['flags' => ['refresh-empty']],
141  ],
142  [
143  'https://example.org/typo3/login',
144  'https://example.org.security/?eID=handler',
145  ['flags' => ['refresh-same-site']],
146  ],
147  [
148  'https://example.org/typo3/login',
149  'https://other-example.site/security/',
150  null,
151  ],
152  ];
153  }
154 
158  #[DataProvider('invalidReferrerIsHandledDataProvider')]
159  #[Test]
160  public function ‪invalidReferrerIsHandled(string $requestUri, string $referrer, ?array $options): void
161  {
162  $this->expectException(InvalidReferrerException::class);
163  $this->expectExceptionCode(1588095936);
164  $subject = $this->‪buildSubject($requestUri, $referrer);
165  $subject->handle($options);
166  }
167 
168  #[Test]
169  public function ‪missingReferrerIsHandled(): void
170  {
171  $this->expectException(MissingReferrerException::class);
172  $this->expectExceptionCode(1588095935);
173  $subject = $this->‪buildSubject(
174  'https://example.org/typo3/login',
175  ''
176  );
177  $subject->handle();
178  }
179 
180  #[Test]
181  public function ‪nonceIsAppliedToResponse(): void
182  {
183  $nonce = new ‪ConsumableNonce();
184  $subject = $this->‪buildSubject(
185  'https://example.org/typo3/login',
186  '',
187  $nonce
188  );
189  $response = $subject->handle(['flags' => ['refresh-always']]);
190  self::assertStringContainsString(
191  'nonce="' . htmlspecialchars($nonce->value) . '">',
192  (string)$response->getBody()
193  );
194  }
195 
196  private function ‪buildSubject(string $requestUri, string $referrer, ‪ConsumableNonce $nonce = null): ‪ReferrerEnforcer
197  {
198  $requestUriInstance = new ‪Uri($requestUri);
199  $host = sprintf(
200  '%s://%s',
201  $requestUriInstance->getScheme(),
202  $requestUriInstance->getHost()
203  );
204  ‪$dir = $host . rtrim(dirname($requestUriInstance->getPath()), '/') . '/';
205  parse_str($requestUriInstance->getQuery(), $queryParams);
206 
207  $normalizedParams = $this->createMock(NormalizedParams::class);
208  $normalizedParams->method('getRequestHost')->willReturn($host);
209  $normalizedParams->method('getRequestDir')->willReturn(‪$dir);
210  $request = $this->createMock(ServerRequestInterface::class);
211  $request->method('getAttribute')->willReturnCallback(static fn(string $name): mixed => match ($name) {
212  'normalizedParams' => $normalizedParams,
213  'nonce' => $nonce,
214  default => null,
215  });
216  $request->method('getServerParams')->willReturn(['HTTP_REFERER' => $referrer]);
217  $request->method('getUri')->willReturn($requestUriInstance);
218  $request->method('getQueryParams')->willReturn($queryParams);
219 
220  $mock = $this->getMockBuilder(ReferrerEnforcer::class)
221  ->onlyMethods(['resolveAbsoluteWebPath'])
222  ->setConstructorArgs([$request])
223  ->getMock();
224  $mock->method('resolveAbsoluteWebPath')->willReturnCallback(static fn(string $target): string => '/' . $target);
225  return $mock;
226  }
227 }
‪TYPO3\CMS\Core\Http\Security\ReferrerEnforcer
Definition: ReferrerEnforcer.php:32
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\validReferrerIsHandledDataProvider
‪static validReferrerIsHandledDataProvider()
Definition: ReferrerEnforcerTest.php:44
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\buildRefreshContentPattern
‪static buildRefreshContentPattern(string $uri)
Definition: ReferrerEnforcerTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\buildSubject
‪buildSubject(string $requestUri, string $referrer, ConsumableNonce $nonce=null)
Definition: ReferrerEnforcerTest.php:196
‪TYPO3\CMS\Core\Security\ContentSecurityPolicy\ConsumableNonce
Definition: ConsumableNonce.php:24
‪$dir
‪$dir
Definition: validateRstFiles.php:257
‪TYPO3\CMS\Core\Http\Security\MissingReferrerException
Definition: MissingReferrerException.php:23
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\invalidReferrerIsHandled
‪invalidReferrerIsHandled(string $requestUri, string $referrer, ?array $options)
Definition: ReferrerEnforcerTest.php:160
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:30
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\validReferrerIsHandled
‪validReferrerIsHandled(string $requestUri, string $referrer, ?array $options, ?string $expectedResponse)
Definition: ReferrerEnforcerTest.php:117
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\missingReferrerIsHandled
‪missingReferrerIsHandled()
Definition: ReferrerEnforcerTest.php:169
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\nonceIsAppliedToResponse
‪nonceIsAppliedToResponse()
Definition: ReferrerEnforcerTest.php:181
‪TYPO3\CMS\Core\Tests\Unit\Http\Security
Definition: ReferrerEnforcerTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest\invalidReferrerIsHandledDataProvider
‪static invalidReferrerIsHandledDataProvider()
Definition: ReferrerEnforcerTest.php:129
‪TYPO3\CMS\Core\Http\Security\InvalidReferrerException
Definition: InvalidReferrerException.php:23
‪TYPO3\CMS\Core\Tests\Unit\Http\Security\ReferrerEnforcerTest
Definition: ReferrerEnforcerTest.php:32
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38