‪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 Psr\Log\NullLogger;
32 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
33 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
34 
35 final class ‪RedirectUrlValidatorTest extends UnitTestCase
36 {
37  protected bool ‪$backupEnvironment = true;
38 
39  protected ‪RedirectUrlValidator&AccessibleObjectInterface ‪$accessibleFixture;
41  protected string ‪$testHostName;
42  protected string ‪$testSitePath;
43 
44  protected bool ‪$resetSingletonInstances = true;
45 
46  protected function ‪setUp(): void
47  {
48  parent::setUp();
49 
50  $site1 = new ‪Site('dummy', 1, ['base' => 'http://sub.domainhostname.tld/path/']);
51  $site2 = new ‪Site('dummy', 1, ['base' => 'http://sub2.domainhostname.tld/']);
52  $mockedSiteFinder = $this->getAccessibleMock(SiteFinder::class, ['getAllSites'], [], '', false, false);
53  $mockedSiteFinder->method('getAllSites')->willReturn([$site1, $site2]);
54 
55  $this->testHostName = 'hostname.tld';
56  $this->testSitePath = '/';
57  $this->accessibleFixture = $this->getAccessibleMock(RedirectUrlValidator::class, null, [$mockedSiteFinder]);
58  $this->accessibleFixture->setLogger(new NullLogger());
60  }
61 
65  protected function ‪setUpFakeSitePathAndHost(): void
66  {
67  $_SERVER['SCRIPT_NAME'] = $this->testSitePath . 'index.php';
68  $_SERVER['HTTP_HOST'] = ‪$this->testHostName;
69 
71  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
72  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
73  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
74  $this->extbaseRequest = new ‪Request($request);
75  }
76 
80  public static function ‪validateRedirectUrlClearsUrlDataProvider(): array
81  {
82  return [
83  'absolute URL, hostname not in site, trailing slash' => ['http://badhost.tld/'],
84  'absolute URL, hostname not in site, no trailing slash' => ['http://badhost.tld'],
85  'absolute URL, subdomain in site, but main domain not, trailing slash' => ['http://domainhostname.tld.badhost.tld/'],
86  'absolute URL, subdomain in site, but main domain not, no trailing slash' => ['http://domainhostname.tld.badhost.tld'],
87  'non http absolute URL 1' => ['its://domainhostname.tld/itunes/'],
88  'non http absolute URL 2' => ['ftp://domainhostname.tld/download/'],
89  'XSS attempt 1' => ['javascript:alert(123)'],
90  'XSS attempt 2' => ['" onmouseover="alert(123)"'],
91  'invalid URL, HTML break out attempt' => ['" >blabuubb'],
92  'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
93  'invalid URL, backslashes in path' => ['http://domainhostname.tld\\bla\\blupp'],
94  'invalid URL, linefeed in path' => ['http://domainhostname.tld/bla/blupp' . LF],
95  'invalid URL, only one slash after scheme' => ['http:/domainhostname.tld/bla/blupp'],
96  'invalid URL, illegal chars' => ['http://(<>domainhostname).tld/bla/blupp'],
97  ];
98  }
99 
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 
142  public function ‪validateRedirectUrlKeepsCleanUrl(string ‪$url): void
143  {
146  true,
147  false,
152  ‪Environment::getPublicPath() . '/index.php',
153  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
154  );
155  self::assertTrue($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
156  }
157 
162  {
163  return [
164  'absolute URL, missing subdirectory' => ['http://hostname.tld/'],
165  'absolute URL, wrong subdirectory' => ['http://hostname.tld/hacker/index.php'],
166  'absolute URL, correct subdirectory, no trailing slash' => ['http://hostname.tld/subdir'],
167  'relative URL, leading slash, no path' => ['/index.php?id=1'],
168  'relative URL, leading slash, wrong path' => ['/de/sub/site.html'],
169  'relative URL, leading slash, slash only' => ['/'],
170  ];
171  }
172 
178  {
179  GeneralUtility::flushInternalRuntimeCaches();
180  $this->testSitePath = '/subdir/';
182  self::assertFalse($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
183  }
184 
189  {
190  return [
191  'absolute URL, correct subdirectory' => ['http://hostname.tld/subdir/'],
192  'absolute URL, correct subdirectory, routing' => ['http://hostname.tld/subdir/de/imprint.html'],
193  'absolute URL, correct subdirectory, no routing' => ['http://hostname.tld/subdir/index.php?id=10'],
194  'absolute URL, correct subdirectory of site base' => ['http://sub.domainhostname.tld/path/'],
195  'relative URL, no leading slash, routing' => ['de/service/imprint.html'],
196  'relative URL, no leading slash, no routing' => ['index.php?id=1'],
197  'relative nested URL, no leading slash, no routing' => ['foo/bar/index.php?id=2'],
198  ];
199  }
200 
206  {
209  true,
210  false,
215  ‪Environment::getPublicPath() . '/index.php',
216  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
217  );
218  $this->testSitePath = '/subdir/';
220  self::assertTrue($this->accessibleFixture->isValid($this->extbaseRequest, ‪$url));
221  }
222 
223  /**************************************************
224  * Tests concerning isInCurrentDomain
225  **************************************************/
226 
230  public static function ‪isInCurrentDomainIgnoresSchemeDataProvider(): array
231  {
232  return [
233  'url https, current host http' => [
234  'example.com', // HTTP_HOST
235  '0', // HTTPS
236  'https://example.com/foo.html', // URL
237  ],
238  'url http, current host https' => [
239  'example.com',
240  '1',
241  'http://example.com/foo.html',
242  ],
243  'url https, current host https' => [
244  'example.com',
245  '1',
246  'https://example.com/foo.html',
247  ],
248  'url http, current host http' => [
249  'example.com',
250  '0',
251  'http://example.com/foo.html',
252  ],
253  ];
254  }
255 
263  public function ‪isInCurrentDomainIgnoresScheme(string $host, string $https, string ‪$url): void
264  {
267  true,
268  false,
273  ‪Environment::getPublicPath() . '/index.php',
274  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
275  );
276  $_SERVER['HTTP_HOST'] = $host;
277  $_SERVER['HTTPS'] = $https;
278 
280  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
281  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
282  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
283  ‪$extbaseRequest = new ‪Request($request);
284 
285  self::assertTrue($this->accessibleFixture->_call('isInCurrentDomain', ‪$extbaseRequest, ‪$url));
286  }
287 
289  {
290  return [
291  'simple difference' => [
292  'example.com', // HTTP_HOST
293  'http://typo3.org/foo.html', // URL
294  ],
295  'subdomain different' => [
296  'example.com',
297  'http://foo.example.com/bar.html',
298  ],
299  ];
300  }
301 
308  public function ‪isInCurrentDomainReturnsFalseIfDomainsAreDifferent(string $host, string ‪$url): void
309  {
310  $_SERVER['HTTP_HOST'] = $host;
311 
313  $request = $request->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
314  $normalizedParams = ‪NormalizedParams::createFromRequest($request);
315  $request = $request->withAttribute('normalizedParams', $normalizedParams)->withAttribute('extbase', new ‪ExtbaseRequestParameters());
316  ‪$extbaseRequest = new ‪Request($request);
317 
318  self::assertFalse($this->accessibleFixture->_call('isInCurrentDomain', ‪$extbaseRequest, ‪$url));
319  }
320 
321  /**************************************************
322  * Tests concerning isInLocalDomain
323  **************************************************/
324 
328  public function ‪isInLocalDomainValidatesSites(): void
329  {
330  ‪$url = 'http://example.com';
331  self::assertFalse($this->accessibleFixture->_call('isInLocalDomain', ‪$url));
332 
333  ‪$url = 'http://sub2.domainhostname.tld/some/path';
334  self::assertTrue($this->accessibleFixture->_call('isInLocalDomain', ‪$url));
335  }
336 }
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation
Definition: RedirectUrlValidatorTest.php:18
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInLocalDomainValidatesSites
‪isInLocalDomainValidatesSites()
Definition: RedirectUrlValidatorTest.php:328
‪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:188
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$testHostName
‪string $testHostName
Definition: RedirectUrlValidatorTest.php:41
‪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:288
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest
Definition: RedirectUrlValidatorTest.php:36
‪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:42
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\isInCurrentDomainIgnoresScheme
‪isInCurrentDomainIgnoresScheme(string $host, string $https, string $url)
Definition: RedirectUrlValidatorTest.php:263
‪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:142
‪TYPO3\CMS\Core\Http\ServerRequestFactory
Definition: ServerRequestFactory.php:34
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$accessibleFixture
‪RedirectUrlValidator &AccessibleObjectInterface $accessibleFixture
Definition: RedirectUrlValidatorTest.php:39
‪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:40
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\setUp
‪setUp()
Definition: RedirectUrlValidatorTest.php:46
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\validateRedirectUrlClearsInvalidUrlInSubdirectory
‪validateRedirectUrlClearsInvalidUrlInSubdirectory(string $url)
Definition: RedirectUrlValidatorTest.php:177
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: RedirectUrlValidatorTest.php:44
‪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:65
‪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:161
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪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:308
‪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:205
‪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:230
‪TYPO3\CMS\FrontendLogin\Tests\Unit\Validation\RedirectUrlValidatorTest\$backupEnvironment
‪bool $backupEnvironment
Definition: RedirectUrlValidatorTest.php:37
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38
‪TYPO3\CMS\Core\Http\ServerRequestFactory\fromGlobals
‪static ServerRequest fromGlobals()
Definition: ServerRequestFactory.php:58
‪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:80