[TASK] Migrate Redirect Url Validator into own class
[Packages/TYPO3.CMS.git] / typo3 / sysext / felogin / Tests / Unit / Validation / RedirectUrlValidatorTest.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Felogin\Tests\Unit\Validation;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use Psr\Log\NullLogger;
20 use TYPO3\CMS\Core\Core\Environment;
21 use TYPO3\CMS\Core\Site\Entity\Site;
22 use TYPO3\CMS\Core\Site\SiteFinder;
23 use TYPO3\CMS\Felogin\Validation\RedirectUrlValidator;
24 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
25
26 /**
27 * Test case
28 */
29 class RedirectUrlValidatorTest extends UnitTestCase
30 {
31 /**
32 * @var bool Restore Environment after tests
33 */
34 protected $backupEnvironment = true;
35
36 /**
37 * @var RedirectUrlValidator|\TYPO3\TestingFramework\Core\AccessibleObjectInterface
38 */
39 protected $accessibleFixture;
40
41 /**
42 * @var string
43 */
44 protected $testHostName;
45
46 /**
47 * @var string
48 */
49 protected $testSitePath;
50
51 protected $resetSingletonInstances = true;
52
53 /**
54 * Set up
55 */
56 protected function setUp()
57 {
58 parent::setUp();
59
60 $site = new Site('dummy', 1, ['base' => 'http://sub.domainhostname.tld/path/']);
61 $mockedSiteFinder = $this->getAccessibleMock(SiteFinder::class, ['getSiteByPageId'], [], '', false, false);
62 $mockedSiteFinder->method('getSiteByPageId')->willReturn($site);
63
64 $this->testHostName = 'hostname.tld';
65 $this->testSitePath = '/';
66 $this->accessibleFixture = $this->getAccessibleMock(RedirectUrlValidator::class, ['dummy'], [$mockedSiteFinder, 1]);
67 $this->accessibleFixture->setLogger(new NullLogger());
68 $this->setUpFakeSitePathAndHost();
69 }
70
71 /**
72 * Set up a fake site path and host
73 */
74 protected function setUpFakeSitePathAndHost()
75 {
76 $_SERVER['ORIG_PATH_INFO'] = $_SERVER['PATH_INFO'] = $_SERVER['ORIG_SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME'] = $this->testSitePath . TYPO3_mainDir;
77 $_SERVER['HTTP_HOST'] = $this->testHostName;
78 }
79
80 /**
81 * Data provider for validateRedirectUrlClearsUrl
82 *
83 * @return array
84 */
85 public function validateRedirectUrlClearsUrlDataProvider()
86 {
87 return [
88 'absolute URL, hostname not in site, trailing slash' => ['http://badhost.tld/'],
89 'absolute URL, hostname not in site, no trailing slash' => ['http://badhost.tld'],
90 'absolute URL, subdomain in site, but main domain not, trailing slash' => ['http://domainhostname.tld.badhost.tld/'],
91 'absolute URL, subdomain in site, but main domain not, no trailing slash' => ['http://domainhostname.tld.badhost.tld'],
92 'non http absolute URL 1' => ['its://domainhostname.tld/itunes/'],
93 'non http absolute URL 2' => ['ftp://domainhostname.tld/download/'],
94 'XSS attempt 1' => ['javascript:alert(123)'],
95 'XSS attempt 2' => ['" onmouseover="alert(123)"'],
96 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
97 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
98 'invalid URL, backslashes in path' => ['http://domainhostname.tld\\bla\\blupp'],
99 'invalid URL, linefeed in path' => ['http://domainhostname.tld/bla/blupp' . LF],
100 'invalid URL, only one slash after scheme' => ['http:/domainhostname.tld/bla/blupp'],
101 'invalid URL, illegal chars' => ['http://(<>domainhostname).tld/bla/blupp'],
102 ];
103 }
104
105 /**
106 * @test
107 * @dataProvider validateRedirectUrlClearsUrlDataProvider
108 * @param string $url Invalid Url
109 */
110 public function validateRedirectUrlClearsUrl($url)
111 {
112 Environment::initialize(
113 Environment::getContext(),
114 true,
115 false,
116 Environment::getProjectPath(),
117 Environment::getPublicPath(),
118 Environment::getVarPath(),
119 Environment::getConfigPath(),
120 Environment::getBackendPath() . '/index.php',
121 Environment::isWindows() ? 'WINDOWS' : 'UNIX'
122 );
123 $this->assertFalse($this->accessibleFixture->isValid($url));
124 }
125
126 /**
127 * Data provider for validateRedirectUrlKeepsCleanUrl
128 *
129 * @return array
130 */
131 public function validateRedirectUrlKeepsCleanUrlDataProvider()
132 {
133 return [
134 'sane absolute URL' => ['http://sub.domainhostname.tld/path/'],
135 'sane absolute URL with script' => ['http://sub.domainhostname.tld/path/index.php?id=1'],
136 'sane absolute URL with realurl' => ['http://sub.domainhostname.tld/path/foo/bar/foo.html'],
137 'sane absolute URL with homedir' => ['http://sub.domainhostname.tld/path/~user/'],
138 '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'],
139 'relative URL, no leading slash 1' => ['index.php?id=1'],
140 'relative URL, no leading slash 2' => ['foo/bar/index.php?id=2'],
141 'relative URL, leading slash, no realurl' => ['/index.php?id=1'],
142 'relative URL, leading slash, realurl' => ['/de/service/imprint.html'],
143 ];
144 }
145
146 /**
147 * @test
148 * @dataProvider validateRedirectUrlKeepsCleanUrlDataProvider
149 * @param string $url Clean URL to test
150 */
151 public function validateRedirectUrlKeepsCleanUrl($url)
152 {
153 Environment::initialize(
154 Environment::getContext(),
155 true,
156 false,
157 Environment::getProjectPath(),
158 Environment::getPublicPath(),
159 Environment::getVarPath(),
160 Environment::getConfigPath(),
161 Environment::getBackendPath() . '/index.php',
162 Environment::isWindows() ? 'WINDOWS' : 'UNIX'
163 );
164 $this->assertTrue($this->accessibleFixture->isValid($url));
165 }
166
167 /**
168 * Data provider for validateRedirectUrlClearsInvalidUrlInSubdirectory
169 *
170 * @return array
171 */
172 public function validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider()
173 {
174 return [
175 'absolute URL, missing subdirectory' => ['http://hostname.tld/'],
176 'absolute URL, wrong subdirectory' => ['http://hostname.tld/hacker/index.php'],
177 'absolute URL, correct subdirectory, no trailing slash' => ['http://hostname.tld/subdir'],
178 'relative URL, leading slash, no path' => ['/index.php?id=1'],
179 'relative URL, leading slash, wrong path' => ['/de/sub/site.html'],
180 'relative URL, leading slash, slash only' => ['/'],
181 ];
182 }
183
184 /**
185 * @test
186 * @dataProvider validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider
187 * @param string $url Invalid Url
188 */
189 public function validateRedirectUrlClearsInvalidUrlInSubdirectory($url)
190 {
191 $this->testSitePath = '/subdir/';
192 $this->setUpFakeSitePathAndHost();
193 $this->assertFalse($this->accessibleFixture->isValid($url));
194 }
195
196 /**
197 * Data provider for validateRedirectUrlKeepsCleanUrlInSubdirectory
198 *
199 * @return array
200 */
201 public function validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider()
202 {
203 return [
204 'absolute URL, correct subdirectory' => ['http://hostname.tld/subdir/'],
205 'absolute URL, correct subdirectory, realurl' => ['http://hostname.tld/subdir/de/imprint.html'],
206 'absolute URL, correct subdirectory, no realurl' => ['http://hostname.tld/subdir/index.php?id=10'],
207 'absolute URL, correct subdirectory of site base' => ['http://sub.domainhostname.tld/path/'],
208 'relative URL, no leading slash, realurl' => ['de/service/imprint.html'],
209 'relative URL, no leading slash, no realurl' => ['index.php?id=1'],
210 'relative nested URL, no leading slash, no realurl' => ['foo/bar/index.php?id=2']
211 ];
212 }
213
214 /**
215 * @test
216 * @dataProvider validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider
217 * @param string $url Invalid Url
218 */
219 public function validateRedirectUrlKeepsCleanUrlInSubdirectory($url)
220 {
221 Environment::initialize(
222 Environment::getContext(),
223 true,
224 false,
225 Environment::getProjectPath(),
226 Environment::getPublicPath(),
227 Environment::getVarPath(),
228 Environment::getConfigPath(),
229 Environment::getBackendPath() . '/index.php',
230 Environment::isWindows() ? 'WINDOWS' : 'UNIX'
231 );
232 $this->testSitePath = '/subdir/';
233 $this->setUpFakeSitePathAndHost();
234 $this->assertTrue($this->accessibleFixture->isValid($url));
235 }
236
237 /**************************************************
238 * Tests concerning isInLocalDomain
239 **************************************************/
240
241 /**
242 * Dataprovider for isInCurrentDomainIgnoresScheme
243 *
244 * @return array
245 */
246 public function isInCurrentDomainIgnoresSchemeDataProvider()
247 {
248 return [
249 'url https, current host http' => [
250 'example.com', // HTTP_HOST
251 '0', // HTTPS
252 'https://example.com/foo.html' // URL
253 ],
254 'url http, current host https' => [
255 'example.com',
256 '1',
257 'http://example.com/foo.html'
258 ],
259 'url https, current host https' => [
260 'example.com',
261 '1',
262 'https://example.com/foo.html'
263 ],
264 'url http, current host http' => [
265 'example.com',
266 '0',
267 'http://example.com/foo.html'
268 ]
269 ];
270 }
271
272 /**
273 * @test
274 * @dataProvider isInCurrentDomainIgnoresSchemeDataProvider
275 * @param string $host $_SERVER['HTTP_HOST']
276 * @param string $https $_SERVER['HTTPS']
277 * @param string $url The url to test
278 */
279 public function isInCurrentDomainIgnoresScheme($host, $https, $url)
280 {
281 Environment::initialize(
282 Environment::getContext(),
283 true,
284 false,
285 Environment::getProjectPath(),
286 Environment::getPublicPath(),
287 Environment::getVarPath(),
288 Environment::getConfigPath(),
289 Environment::getBackendPath() . '/index.php',
290 Environment::isWindows() ? 'WINDOWS' : 'UNIX'
291 );
292 $_SERVER['HTTP_HOST'] = $host;
293 $_SERVER['HTTPS'] = $https;
294 $this->assertTrue($this->accessibleFixture->_call('isInCurrentDomain', $url));
295 }
296
297 /**
298 * @return array
299 */
300 public function isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider()
301 {
302 return [
303 'simple difference' => [
304 'example.com', // HTTP_HOST
305 'http://typo3.org/foo.html' // URL
306 ],
307 'subdomain different' => [
308 'example.com',
309 'http://foo.example.com/bar.html'
310 ]
311 ];
312 }
313
314 /**
315 * @test
316 * @dataProvider isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider
317 * @param string $host $_SERVER['HTTP_HOST']
318 * @param string $url The url to test
319 */
320 public function isInCurrentDomainReturnsFalseIfDomainsAreDifferent($host, $url)
321 {
322 $_SERVER['HTTP_HOST'] = $host;
323 $this->assertFalse($this->accessibleFixture->_call('isInCurrentDomain', $url));
324 }
325 }