‪TYPO3CMS  10.4
FileNameValidatorTest.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\TestCase;
23 
24 class ‪FileNameValidatorTest extends TestCase
25 {
30  {
31  return [
32  'Nul character in file' => ['image' . "\0" . '.gif'],
33  'Nul character in file with .php' => ['image.php' . "\0" . '.gif'],
34  'Nul character and UTF-8 in file' => ['Ссылка' . "\0" . '.gif'],
35  'Nul character and Latin-1 in file' => ['ÉÐØ' . "\0" . '.gif'],
36  ];
37  }
38 
46  public function ‪verifyNulCharacterFilesAgainstPatternWithoutFileDenyPattern(string $deniedFile): void
47  {
48  $subject = new ‪FileNameValidator('');
49  self::assertFalse($subject->isValid($deniedFile));
50 
51  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] = '';
52  $subject = new ‪FileNameValidator();
53  self::assertFalse($subject->isValid($deniedFile));
54  }
55 
60  {
61  $data = [
62  'Nul character in file' => ['image' . "\0", '.gif'],
63  'Nul character in file with .php' => ['image.php' . "\0", '.gif'],
64  'Nul character and UTF-8 in file' => ['Ссылка' . "\0", '.gif'],
65  'Nul character and Latin-1 in file' => ['ÉÐØ' . "\0", '.gif'],
66  'Lower umlaut .php file' => ['üWithFile', '.php'],
67  'Upper umlaut .php file' => ['fileWithÜ', '.php'],
68  'invalid UTF-8-sequence' => ["\xc0" . 'file', '.php'],
69  'Could be overlong NUL in some UTF-8 implementations, invalid in RFC3629' => ["\xc0\x80" . 'file', '.php'],
70  'Regular .php file' => ['file', '.php'],
71  'Regular .php3 file' => ['file', '.php3'],
72  'Regular .php5 file' => ['file', '.php5'],
73  'Regular .php7 file' => ['file', '.php7'],
74  'Regular .phpsh file' => ['file', '.phpsh'],
75  'Regular .phtml file' => ['file', '.phtml'],
76  'Regular .pht file' => ['file', '.pht'],
77  'Regular .phar file' => ['file', '.phar'],
78  'Regular .shtml file' => ['file', '.shtml'],
79  'Regular .cgi file' => ['file', '.cgi'],
80  'Regular .pl file' => ['file', '.pl'],
81  'Wrapped .php file ' => ['file', '.php.txt'],
82  'Wrapped .php3 file' => ['file', '.php3.txt'],
83  'Wrapped .php5 file' => ['file', '.php5.txt'],
84  'Wrapped .php7 file' => ['file', '.php7.txt'],
85  'Wrapped .phpsh file' => ['file', '.phpsh.txt'],
86  'Wrapped .phtml file' => ['file', '.phtml.txt'],
87  'Wrapped .pht file' => ['file', '.pht.txt'],
88  'Wrapped .phar file' => ['file', '.phar.txt'],
89  'Wrapped .shtml file' => ['file', '.shtml.txt'],
90  'Wrapped .cgi file' => ['file', '.cgi.txt'],
91  // allowed "Wrapped .pl file" in order to allow language specific files containing ".pl."
92  '.htaccess file' => ['', '.htaccess'],
93  ];
94 
95  // Mixing with regular utf-8
96  $utf8Characters = 'Ссылка';
97  foreach ($data as $key => $value) {
98  if ($value[0] === '') {
99  continue;
100  }
101  $data[$key . ' with UTF-8 characters prepended'] = [$utf8Characters . $value[0], $value[1]];
102  $data[$key . ' with UTF-8 characters appended'] = [$value[0] . $utf8Characters, $value[1]];
103  }
104 
105  // combine to single value
106  $data = array_map(
107  function (array $values): array {
108  return [implode('', $values)];
109  },
110  $data
111  );
112 
113  // Encoding with UTF-16
114  foreach ($data as $key => $value) {
115  $data[$key . ' encoded with UTF-16'] = [mb_convert_encoding($value[0], 'UTF-16')];
116  }
117 
118  return $data;
119  }
120 
128  public function ‪isValidDetectsNotAllowedFiles(string $deniedFile): void
129  {
130  $subject = new ‪FileNameValidator();
131  self::assertFalse($subject->isValid($deniedFile));
132  }
133 
137  public function ‪insecureFilesDataProvider(): array
138  {
139  return [
140  'Classic php file' => ['user.php'],
141  'A random .htaccess file' => ['.htaccess'],
142  'Wrapped .php file' => ['file.php.txt'],
143  ];
144  }
145 
151  public function ‪isValidAcceptsNotAllowedFilesDueToInsecureSetting(string $fileName): void
152  {
153  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] = '\\.phc$';
154  $subject = new ‪FileNameValidator();
155  self::assertTrue($subject->isValid($fileName));
156  }
157 
161  public function ‪allowedFilesDataProvider(): array
162  {
163  return [
164  'Regular .gif file' => ['image.gif'],
165  'Regular uppercase .gif file' => ['IMAGE.gif'],
166  'UTF-8 .gif file' => ['Ссылка.gif'],
167  'Lower umlaut .jpg file' => ['üWithFile.jpg'],
168  'Upper umlaut .png file' => ['fileWithÜ.png'],
169  'Latin-1 .gif file' => ['ÉÐØ.gif'],
170  'Wrapped .pl file' => ['file.pl.txt'],
171  ];
172  }
173 
181  public function ‪isValidAcceptAllowedFiles(string $allowedFile): void
182  {
183  $subject = new ‪FileNameValidator();
184  self::assertTrue($subject->isValid($allowedFile));
185  }
186 
190  public function ‪isCustomDenyPatternConfigured(): void
191  {
192  $subject = new ‪FileNameValidator('nothing-really');
193  self::assertTrue($subject->customFileDenyPatternConfigured());
194  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] = 'something-else';
195  $subject = new ‪FileNameValidator();
196  self::assertTrue($subject->customFileDenyPatternConfigured());
197  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] = ‪FileNameValidator::DEFAULT_FILE_DENY_PATTERN;
198  $subject = new ‪FileNameValidator();
199  self::assertFalse($subject->customFileDenyPatternConfigured());
201  self::assertFalse($subject->customFileDenyPatternConfigured());
202  }
203 
208  {
209  $subject = new ‪FileNameValidator('\\.php$|.php8$');
210  self::assertTrue($subject->missingImportantPatterns());
212  self::assertFalse($subject->missingImportantPatterns());
213  }
214 
220  public function ‪phpExtensionDataProvider(): array
221  {
222  $data = [];
223  $fileName = ‪StringUtility::getUniqueId('filename');
224  $phpExtensions = ['php', 'php3', 'php4', 'php5', 'php7', 'phpsh', 'phtml', 'pht'];
225  foreach ($phpExtensions as $extension) {
226  $data[] = [$fileName . '.' . $extension];
227  $data[] = [$fileName . '.' . $extension . '.txt'];
228  }
229  return $data;
230  }
231 
239  public function ‪defaultFileDenyPatternMatchesPhpExtension(string $fileName): void
240  {
241  self::assertGreaterThan(0, preg_match('/' . ‪FileNameValidator::DEFAULT_FILE_DENY_PATTERN . '/', $fileName), $fileName);
242  }
243 
251  public function ‪invalidPhpExtensionIsDetected(string $fileName): void
252  {
253  $subject = new ‪FileNameValidator();
254  self::assertFalse($subject->isValid($fileName));
255  }
256 }
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator\DEFAULT_FILE_DENY_PATTERN
‪const DEFAULT_FILE_DENY_PATTERN
Definition: FileNameValidator.php:29
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\isCustomDenyPatternConfigured
‪isCustomDenyPatternConfigured()
Definition: FileNameValidatorTest.php:190
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\insecureFilesDataProvider
‪array insecureFilesDataProvider()
Definition: FileNameValidatorTest.php:137
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\verifyNulCharacterFilesAgainstPatternWithoutFileDenyPattern
‪verifyNulCharacterFilesAgainstPatternWithoutFileDenyPattern(string $deniedFile)
Definition: FileNameValidatorTest.php:46
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\allowedFilesDataProvider
‪array allowedFilesDataProvider()
Definition: FileNameValidatorTest.php:161
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security
Definition: FileNameValidatorTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\invalidPhpExtensionIsDetected
‪invalidPhpExtensionIsDetected(string $fileName)
Definition: FileNameValidatorTest.php:251
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\phpExtensionDataProvider
‪array phpExtensionDataProvider()
Definition: FileNameValidatorTest.php:220
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\customFileDenyPatternFindsMissingImportantParts
‪customFileDenyPatternFindsMissingImportantParts()
Definition: FileNameValidatorTest.php:207
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest
Definition: FileNameValidatorTest.php:25
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\isValidAcceptAllowedFiles
‪isValidAcceptAllowedFiles(string $allowedFile)
Definition: FileNameValidatorTest.php:181
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:92
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\defaultFileDenyPatternMatchesPhpExtension
‪defaultFileDenyPatternMatchesPhpExtension(string $fileName)
Definition: FileNameValidatorTest.php:239
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\isValidAcceptsNotAllowedFilesDueToInsecureSetting
‪isValidAcceptsNotAllowedFilesDueToInsecureSetting(string $fileName)
Definition: FileNameValidatorTest.php:151
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\deniedFilesWithoutDenyPatternDataProvider
‪array deniedFilesWithoutDenyPatternDataProvider()
Definition: FileNameValidatorTest.php:29
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\isValidDetectsNotAllowedFiles
‪isValidDetectsNotAllowedFiles(string $deniedFile)
Definition: FileNameValidatorTest.php:128
‪TYPO3\CMS\Core\Tests\Unit\Resource\Security\FileNameValidatorTest\deniedFilesWithDefaultDenyPatternDataProvider
‪array deniedFilesWithDefaultDenyPatternDataProvider()
Definition: FileNameValidatorTest.php:59