diff options
Diffstat (limited to 'tests/lib/Files/FilenameValidatorTest.php')
-rw-r--r-- | tests/lib/Files/FilenameValidatorTest.php | 244 |
1 files changed, 224 insertions, 20 deletions
diff --git a/tests/lib/Files/FilenameValidatorTest.php b/tests/lib/Files/FilenameValidatorTest.php index ac9ac032b64..162275a2cf8 100644 --- a/tests/lib/Files/FilenameValidatorTest.php +++ b/tests/lib/Files/FilenameValidatorTest.php @@ -48,9 +48,7 @@ class FilenameValidatorTest extends TestCase { $this->database->method('supports4ByteText')->willReturn(true); } - /** - * @dataProvider dataValidateFilename - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')] public function testValidateFilename( string $filename, array $forbiddenNames, @@ -87,9 +85,7 @@ class FilenameValidatorTest extends TestCase { $validator->validateFilename($filename); } - /** - * @dataProvider dataValidateFilename - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')] public function testIsFilenameValid( string $filename, array $forbiddenNames, @@ -122,7 +118,7 @@ class FilenameValidatorTest extends TestCase { $this->assertEquals($exception === null, $validator->isFilenameValid($filename)); } - public function dataValidateFilename(): array { + public static function dataValidateFilename(): array { return [ 'valid name' => [ 'a: b.txt', ['.htaccess'], [], [], [], null @@ -146,6 +142,10 @@ class FilenameValidatorTest extends TestCase { // needed for Windows namespaces 'com1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class ], + 'forbidden basename case insensitive' => [ + // needed for Windows namespaces + 'COM1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class + ], 'forbidden basename for hidden files' => [ // needed for Windows namespaces '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], ReservedWordException::class @@ -159,6 +159,9 @@ class FilenameValidatorTest extends TestCase { 'invalid extension' => [ 'a: b.txt', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class ], + 'invalid extension case insensitive' => [ + 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class + ], 'empty filename' => [ '', [], [], [], [], EmptyFileNameException::class ], @@ -182,9 +185,7 @@ class FilenameValidatorTest extends TestCase { ]; } - /** - * @dataProvider data4ByteUnicode - */ + #[\PHPUnit\Framework\Attributes\DataProvider('data4ByteUnicode')] public function testDatabaseDoesNotSupport4ByteText($filename): void { $database = $this->createMock(IDBConnection::class); $database->expects($this->once()) @@ -195,24 +196,21 @@ class FilenameValidatorTest extends TestCase { $validator->validateFilename($filename); } - public function data4ByteUnicode(): array { + public static function data4ByteUnicode(): array { return [ ['plane 1 πͺ
'], ['emoji πΆβπ«οΈ'], - ]; } - /** - * @dataProvider dataInvalidAsciiCharacters - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidAsciiCharacters')] public function testInvalidAsciiCharactersAreAlwaysForbidden(string $filename): void { $this->expectException(InvalidPathException::class); $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); $validator->validateFilename($filename); } - public function dataInvalidAsciiCharacters(): array { + public static function dataInvalidAsciiCharacters(): array { return [ [\chr(0)], [\chr(1)], @@ -249,9 +247,7 @@ class FilenameValidatorTest extends TestCase { ]; } - /** - * @dataProvider dataIsForbidden - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataIsForbidden')] public function testIsForbidden(string $filename, array $forbiddenNames, bool $expected): void { /** @var FilenameValidator&MockObject */ $validator = $this->getMockBuilder(FilenameValidator::class) @@ -265,7 +261,7 @@ class FilenameValidatorTest extends TestCase { $this->assertEquals($expected, $validator->isForbidden($filename)); } - public function dataIsForbidden(): array { + public static function dataIsForbidden(): array { return [ 'valid name' => [ 'a: b.txt', ['.htaccess'], false @@ -284,4 +280,212 @@ class FilenameValidatorTest extends TestCase { ], ]; } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenExtensions')] + public function testGetForbiddenExtensions(array $configValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::once()) + ->method('getSystemValue') + ->with('forbidden_filename_extensions', ['.filepart']) + ->willReturn($configValue); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenExtensions()); + } + + public static function dataGetForbiddenExtensions(): array { + return [ + // default + [['.filepart'], ['.filepart', '.part']], + // always include .part + [[], ['.part']], + // handle case insensitivity + [['.TXT'], ['.txt', '.part']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenFilenames')] + public function testGetForbiddenFilenames(array $configValue, array $legacyValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::exactly(2)) + ->method('getSystemValue') + ->willReturnMap([ + ['forbidden_filenames', ['.htaccess'], $configValue], + ['blacklisted_files', [], $legacyValue], + ]); + + $this->logger + ->expects(empty($legacyValue) ? self::never() : self::once()) + ->method('warning'); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenFilenames()); + } + + public static function dataGetForbiddenFilenames(): array { + return [ + // default + [['.htaccess'], [], ['.htaccess']], + // with legacy values + [['.htaccess'], ['legacy'], ['.htaccess', 'legacy']], + // handle case insensitivity + [['FileName', '.htaccess'], ['LegAcy'], ['.htaccess', 'filename', 'legacy']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenBasenames')] + public function testGetForbiddenBasenames(array $configValue, array $expectedValue): void { + $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger); + $this->config + // only once - then cached + ->expects(self::once()) + ->method('getSystemValue') + ->with('forbidden_filename_basenames', []) + ->willReturn($configValue); + + self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenBasenames()); + } + + public static function dataGetForbiddenBasenames(): array { + return [ + // default + [[], []], + // with values + [['aux', 'com0'], ['aux', 'com0']], + // handle case insensitivity + [['AuX', 'COM1'], ['aux', 'com1']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilename')] + public function testSanitizeFilename( + string $filename, + array $forbiddenNames, + array $forbiddenBasenames, + array $forbiddenExtensions, + array $forbiddenCharacters, + string $expected, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + 'getForbiddenCharacters', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn($forbiddenBasenames); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn($forbiddenExtensions); + $validator->method('getForbiddenFilenames') + ->willReturn($forbiddenNames); + + $this->assertEquals($expected, $validator->sanitizeFilename($filename)); + } + + public static function dataSanitizeFilename(): array { + return [ + 'valid name' => [ + 'a * b.txt', ['.htaccess'], [], [], [], 'a * b.txt' + ], + 'forbidden name in the middle is ok' => [ + 'a.htaccess.txt', ['.htaccess'], [], [], [], 'a.htaccess.txt' + ], + 'forbidden name on the beginning' => [ + '.htaccess.sample', ['.htaccess'], [], [], [], '.htaccess.sample' + ], + 'forbidden name' => [ + '.htaccess', ['.htaccess'], [], [], [], '.htaccess (renamed)' + ], + 'forbidden name - name is case insensitive' => [ + 'COM1', ['.htaccess', 'com1'], [], [], [], 'COM1 (renamed)' + ], + 'forbidden basename' => [ + 'com1.suffix', ['.htaccess'], ['com1'], [], [], 'com1 (renamed).suffix' + ], + 'forbidden basename case insensitive' => [ + // needed for Windows namespaces + 'COM1.suffix', ['.htaccess'], ['com1'], [], [], 'COM1 (renamed).suffix' + ], + 'forbidden basename for hidden files' => [ + // needed for Windows namespaces + '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], '.thumbs (renamed).db' + ], + 'invalid character' => [ + 'a: b.txt', ['.htaccess'], [], [], [':'], 'a_ b.txt', + ], + 'invalid extension' => [ + 'a: b.txt', ['.htaccess'], [], ['.txt'], [], 'a: b' + ], + 'invalid extension case insensitive' => [ + 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], 'a: b' + ], + 'empty filename' => [ + '', [], [], [], [], 'renamed file' + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilenameCharacterReplacement')] + public function testSanitizeFilenameCharacterReplacement( + string $filename, + array $forbiddenCharacters, + ?string $characterReplacement, + ?string $expected, + ): void { + /** @var FilenameValidator&MockObject */ + $validator = $this->getMockBuilder(FilenameValidator::class) + ->onlyMethods([ + 'getForbiddenBasenames', + 'getForbiddenExtensions', + 'getForbiddenFilenames', + 'getForbiddenCharacters', + ]) + ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger]) + ->getMock(); + + $validator->method('getForbiddenBasenames') + ->willReturn([]); + $validator->method('getForbiddenCharacters') + ->willReturn($forbiddenCharacters); + $validator->method('getForbiddenExtensions') + ->willReturn([]); + $validator->method('getForbiddenFilenames') + ->willReturn([]); + + if ($expected === null) { + $this->expectException(\InvalidArgumentException::class); + $validator->sanitizeFilename($filename, $characterReplacement); + } else { + $this->assertEquals($expected, $validator->sanitizeFilename($filename, $characterReplacement)); + } + } + + public static function dataSanitizeFilenameCharacterReplacement(): array { + return [ + 'default' => [ + 'foo*bar', ['*'], null, 'foo_bar' + ], + 'default - underscore not allowed' => [ + 'foo*bar', ['*', '_'], null, 'foo-bar' + ], + 'default - dash and underscore not allowed' => [ + 'foo*bar', ['*', '-', '_'], null, 'foo bar' + ], + 'default - no replacement' => [ + 'foo*bar', ['*', ' ', '_', '-'], null, null + ], + 'custom replacement' => [ + 'foo*bar', ['*'], 'x', 'fooxbar' + ], + ]; + } } |