‪TYPO3CMS  ‪main
RedirectUrlValidatorTest.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\Log\NullLogger;
34 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
35 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
36 
37 final class ‪RedirectUrlValidatorTest extends UnitTestCase
38 {
39  protected bool ‪$backupEnvironment = true;
40 
41  protected ‪RedirectUrlValidator&AccessibleObjectInterface ‪$accessibleFixture;
43  protected string ‪$testHostName;
44  protected string ‪$testSitePath;
45 
46  protected bool ‪$resetSingletonInstances = true;
47 
48  protected function ‪setUp(): void
49  {
50  parent::setUp();
51 
52  $site1 = new ‪Site('dummy', 1, ['base' => 'http://sub.domainhostname.tld/path/']);
53  $site2 = new ‪Site('dummy', 1, ['base' => 'http://sub2.domainhostname.tld/']);
54  $mockedSiteFinder = $this->getAccessibleMock(SiteFinder::class, ['getAllSites'], [], '', false, false);
55  $mockedSiteFinder->method('getAllSites')->willReturn([$site1, $site2]);
56 
57  $this->testHostName = 'hostname.tld';
58  $this->testSitePath = '/';
59  $this->accessibleFixture = $this->getAccessibleMock(RedirectUrlValidator::class, null, [$mockedSiteFinder]);
60  $this->accessibleFixture->setLogger(new NullLogger());
62  }
63 
67  protected function ‪setUpFakeSitePathAndHost(): void
68  {
69  ‪$_SERVER['SCRIPT_NAME'] = $this->testSitePath . 'index.php';
70  ‪$_SERVER['HTTP_HOST'] = ‪$this->testHostName;
71 
73  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
74  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
75  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
76  $this->extbaseRequest = new ‪Request($request);
77  }
78 
82  public static function ‪validateRedirectUrlClearsUrlDataProvider(): array
83  {
84  return [
85  'absolute URL, hostname not in site, trailing slash' => ['http://badhost.tld/'],
86  'absolute URL, hostname not in site, no trailing slash' => ['http://badhost.tld'],
87  'absolute URL, subdomain in site, but main domain not, trailing slash' => ['http://domainhostname.tld.badhost.tld/'],
88  'absolute URL, subdomain in site, but main domain not, no trailing slash' => ['http://domainhostname.tld.badhost.tld'],
89  'non http absolute URL 1' => ['its://domainhostname.tld/itunes/'],
90  'non http absolute URL 2' => ['ftp://domainhostname.tld/download/'],
91  'XSS attempt 1' => ['javascript:alert(123)'],
92  'XSS attempt 2' => ['" onmouseover="alert(123)"'],
93  'invalid URL, HTML break out attempt' => ['" >blabuubb'],
94  'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
95  'invalid URL, backslashes in path' => ['http://domainhostname.tld\\bla\\blupp'],
96  'invalid URL, linefeed in path' => ['http://domainhostname.tld/bla/blupp' . LF],
97  'invalid URL, only one slash after scheme' => ['http:/domainhostname.tld/bla/blupp'],
98  'invalid URL, illegal chars' => ['http://(<>domainhostname).tld/bla/blupp'],
99  ];
100  }
101 
102  #[DataProvider('validateRedirectUrlClearsUrlDataProvider')]
103  #[Test]
104  public function ‪validateRedirectUrlClearsUrl(string ‪$url): void
105  {
108  true,
109  false,
114  ‪Environment::getPublicPath() . '/index.php',
115  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
116  );
117  self::assertFalse($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
118  }
119 
123  public static function ‪validateRedirectUrlKeepsCleanUrlDataProvider(): array
124  {
125  return [
126  'sane absolute URL' => ['http://sub.domainhostname.tld/path/'],
127  'sane absolute URL with script' => ['http://sub.domainhostname.tld/path/index.php?id=1'],
128  'sane absolute URL with routing' => ['http://sub.domainhostname.tld/path/foo/bar/foo.html'],
129  'sane absolute URL with homedir' => ['http://sub.domainhostname.tld/path/~user/'],
130  'sane absolute URL with some strange chars encoded' => ['http://sub.domainhostname.tld/path/~user/a%cc%88o%cc%88%c3%9fa%cc%82/foo.html'],
131  'relative URL, no leading slash 1' => ['index.php?id=1'],
132  'relative URL, no leading slash 2' => ['foo/bar/index.php?id=2'],
133  'relative URL, leading slash, no routing' => ['/index.php?id=1'],
134  'relative URL, leading slash, routing' => ['/de/service/imprint.html'],
135  ];
136  }
137 
138  #[DataProvider('validateRedirectUrlKeepsCleanUrlDataProvider')]
139  #[Test]
140  public function ‪validateRedirectUrlKeepsCleanUrl(string ‪$url): void
141  {
144  true,
145  false,
150  ‪Environment::getPublicPath() . '/index.php',
151  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
152  );
153  self::assertTrue($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
154  }
155 
160  {
161  return [
162  'absolute URL, missing subdirectory' => ['http://hostname.tld/'],
163  'absolute URL, wrong subdirectory' => ['http://hostname.tld/hacker/index.php'],
164  'absolute URL, correct subdirectory, no trailing slash' => ['http://hostname.tld/subdir'],
165  'relative URL, leading slash, no path' => ['/index.php?id=1'],
166  'relative URL, leading slash, wrong path' => ['/de/sub/site.html'],
167  'relative URL, leading slash, slash only' => ['/'],
168  ];
169  }
170 
171  #[DataProvider('validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider')]
172  #[Test]
174  {
175  GeneralUtility::flushInternalRuntimeCaches();
176  $this->testSitePath = '/subdir/';
178  self::assertFalse($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
179  }
180 
185  {
186  return [
187  'absolute URL, correct subdirectory' => ['http://hostname.tld/subdir/'],
188  'absolute URL, correct subdirectory, routing' => ['http://hostname.tld/subdir/de/imprint.html'],
189  'absolute URL, correct subdirectory, no routing' => ['http://hostname.tld/subdir/index.php?id=10'],
190  'absolute URL, correct subdirectory of site base' => ['http://sub.domainhostname.tld/path/'],
191  'relative URL, no leading slash, routing' => ['de/service/imprint.html'],
192  'relative URL, no leading slash, no routing' => ['index.php?id=1'],
193  'relative nested URL, no leading slash, no routing' => ['foo/bar/index.php?id=2'],
194  ];
195  }
196 
197  #[DataProvider('validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider')]
198  #[Test]
200  {
203  true,
204  false,
209  ‪Environment::getPublicPath() . '/index.php',
210  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
211  );
212  $this->testSitePath = '/subdir/';
214  self::assertTrue($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
215  }
216 
217  /**************************************************
218  * Tests concerning isInCurrentDomain
219  **************************************************/
223  public static function ‪isInCurrentDomainIgnoresSchemeDataProvider(): array
224  {
225  return [
226  'url https, current host http' => [
227  'example.com', // HTTP_HOST
228  '0', // HTTPS
229  'https://example.com/foo.html', // URL
230  ],
231  'url http, current host https' => [
232  'example.com',
233  '1',
234  'http://example.com/foo.html',
235  ],
236  'url https, current host https' => [
237  'example.com',
238  '1',
239  'https://example.com/foo.html',
240  ],
241  'url http, current host http' => [
242  'example.com',
243  '0',
244  'http://example.com/foo.html',
245  ],
246  ];
247  }
248 
254  #[DataProvider('isInCurrentDomainIgnoresSchemeDataProvider')]
255  #[Test]
256  public function ‪isInCurrentDomainIgnoresScheme(string $host, string $https, string ‪$url): void
257  {
260  true,
261  false,
266  ‪Environment::getPublicPath() . '/index.php',
267  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
268  );
269  ‪$_SERVER['HTTP_HOST'] = $host;
270  ‪$_SERVER['HTTPS'] = $https;
271 
273  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
274  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
275  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
276  ‪$extbaseRequest = new ‪Request($request);
277 
278  self::assertTrue($this->accessibleFixture->_call('isInCurrentDomain', ‪$extbaseRequest, ‪$url));
279  }
280 
282  {
283  return [
284  'simple difference' => [
285  'example.com', // HTTP_HOST
286  'http://typo3.org/foo.html', // URL
287  ],
288  'subdomain different' => [
289  'example.com',
290  'http://foo.example.com/bar.html',
291  ],
292  ];
293  }
294 
299  #[DataProvider('isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider')]
300  #[Test]
301  public function ‪isInCurrentDomainReturnsFalseIfDomainsAreDifferent(string $host, string ‪$url): void
302  {
303  ‪$_SERVER['HTTP_HOST'] = $host;
304 
306  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
307  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
308  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
309  ‪$extbaseRequest = new ‪Request($request);
310 
311  self::assertFalse($this->accessibleFixture->_call('isInCurrentDomain', ‪$extbaseRequest, ‪$url));
312  }
313 
314  /**************************************************
315  * Tests concerning isInLocalDomain
316  **************************************************/
317  #[Test]
318  public function ‪isInLocalDomainValidatesSites(): void
319  {
320  ‪$url = 'http://example.com';
321  self::assertFalse($this->accessibleFixture->_call('isInLocalDomain', ‪$url));
322 
323  ‪$url = 'http://sub2.domainhostname.tld/some/path';
324  self::assertTrue($this->accessibleFixture->_call('isInLocalDomain', ‪$url));
325  }
326 }
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation
Definition: RedirectUrlValidatorTest.php:18
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInLocalDomainValidatesSites
‪isInLocalDomainValidatesSites()
Definition: RedirectUrlValidatorTest.php:318
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlKeepsCleanUrlDataProvider
‪static validateRedirectUrlKeepsCleanUrlDataProvider()
Definition: RedirectUrlValidatorTest.php:123
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider
‪static validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider()
Definition: RedirectUrlValidatorTest.php:184
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$testHostName
‪string $testHostName
Definition: RedirectUrlValidatorTest.php:43
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder
Definition: SystemEnvironmentBuilder.php:41
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider
‪static isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider()
Definition: RedirectUrlValidatorTest.php:281
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest
Definition: RedirectUrlValidatorTest.php:38
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$testSitePath
‪string $testSitePath
Definition: RedirectUrlValidatorTest.php:44
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInCurrentDomainIgnoresScheme
‪isInCurrentDomainIgnoresScheme(string $host, string $https, string $url)
Definition: RedirectUrlValidatorTest.php:256
‪TYPO3\CMS\Core\Core\Environment\getConfigPath
‪static getConfigPath()
Definition: Environment.php:212
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlKeepsCleanUrl
‪validateRedirectUrlKeepsCleanUrl(string $url)
Definition: RedirectUrlValidatorTest.php:140
‪TYPO3\CMS\Core\Http\ServerRequestFactory
Definition: ServerRequestFactory.php:35
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$accessibleFixture
‪RedirectUrlValidator &AccessibleObjectInterface $accessibleFixture
Definition: RedirectUrlValidatorTest.php:41
‪TYPO3\CMS\Core\Core\Environment\initialize
‪static initialize(ApplicationContext $context, bool $cli, bool $composerMode, string $projectPath, string $publicPath, string $varPath, string $configPath, string $currentScript, string $os)
Definition: Environment.php:100
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$extbaseRequest
‪RequestInterface $extbaseRequest
Definition: RedirectUrlValidatorTest.php:42
‪$_SERVER
‪$_SERVER['TYPO3_DEPRECATED_ENTRYPOINT']
Definition: legacy-backend.php:20
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\setUp
‪setUp()
Definition: RedirectUrlValidatorTest.php:48
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlClearsInvalidUrlInSubdirectory
‪validateRedirectUrlClearsInvalidUrlInSubdirectory(string $url)
Definition: RedirectUrlValidatorTest.php:173
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: RedirectUrlValidatorTest.php:46
‪TYPO3\CMS\Extbase\Mvc\RequestInterface
Definition: RequestInterface.php:24
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlClearsUrl
‪validateRedirectUrlClearsUrl(string $url)
Definition: RedirectUrlValidatorTest.php:104
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\setUpFakeSitePathAndHost
‪setUpFakeSitePathAndHost()
Definition: RedirectUrlValidatorTest.php:67
‪TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters
Definition: ExtbaseRequestParameters.php:35
‪TYPO3\CMS\FrontendLogin\Validation\RedirectUrlValidator
Definition: RedirectUrlValidator.php:33
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider
‪static validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider()
Definition: RedirectUrlValidatorTest.php:159
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder\REQUESTTYPE_FE
‪const REQUESTTYPE_FE
Definition: SystemEnvironmentBuilder.php:43
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInCurrentDomainReturnsFalseIfDomainsAreDifferent
‪isInCurrentDomainReturnsFalseIfDomainsAreDifferent(string $host, string $url)
Definition: RedirectUrlValidatorTest.php:301
‪TYPO3\CMS\Core\Http\NormalizedParams\createFromRequest
‪static static createFromRequest(ServerRequestInterface $request, array $systemConfiguration=null)
Definition: NormalizedParams.php:840
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlKeepsCleanUrlInSubdirectory
‪validateRedirectUrlKeepsCleanUrlInSubdirectory(string $url)
Definition: RedirectUrlValidatorTest.php:199
‪TYPO3\CMS\Core\Core\Environment\getContext
‪static getContext()
Definition: Environment.php:128
‪TYPO3\CMS\Extbase\Mvc\Request
Definition: Request.php:35
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInCurrentDomainIgnoresSchemeDataProvider
‪static isInCurrentDomainIgnoresSchemeDataProvider()
Definition: RedirectUrlValidatorTest.php:223
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$backupEnvironment
‪bool $backupEnvironment
Definition: RedirectUrlValidatorTest.php:39
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38
‪TYPO3\CMS\Core\Http\ServerRequestFactory\fromGlobals
‪static ServerRequest fromGlobals()
Definition: ServerRequestFactory.php:59
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static isWindows()
Definition: Environment.php:276
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlClearsUrlDataProvider
‪static validateRedirectUrlClearsUrlDataProvider()
Definition: RedirectUrlValidatorTest.php:82