diff --git a/src/Validation/Validator/FileNameValidator.php b/src/Validation/Validator/FileNameValidator.php new file mode 100644 index 0000000..1009d02 --- /dev/null +++ b/src/Validation/Validator/FileNameValidator.php @@ -0,0 +1,52 @@ +:"|?*\\'; + + public function validateFile(FilePathInterface $filePath): void + { + $fileName = $filePath->getFileName(); + + $this->checkFilenameNotEmpty($fileName); + $this->checkFilenameDoesNotStartWithDot($fileName); + $this->checkForbiddenCharacters($fileName); + } + + private function checkForbiddenCharacters(string $fileName): void + { + $forbiddenCharacters = str_split(self::FORBIDDEN_CHARACTERS); + foreach ($forbiddenCharacters as $oneForbiddenCharacter) { + if (strpos($fileName, $oneForbiddenCharacter) !== false) { + throw new ValidationFailedException("Forbidden character found: " . $oneForbiddenCharacter); + } + } + } + + public function checkFilenameNotEmpty(string $fileName): void + { + if (!$fileName) { + throw new ValidationFailedException("Filename cannot be empty"); + } + } + + public function checkFilenameDoesNotStartWithDot(string $fileName): void + { + if ($fileName[0] === '.') { + throw new ValidationFailedException("Filename cannot start with ."); + } + } +} diff --git a/src/Validation/services.yaml b/src/Validation/services.yaml index 70d3e21..4538641 100644 --- a/src/Validation/services.yaml +++ b/src/Validation/services.yaml @@ -8,6 +8,7 @@ services: public: true arguments: $fileValidators: + - '@OxidEsales\MediaLibrary\Validation\Validator\FileNameValidator' - '@OxidEsales\MediaLibrary\Validation\Validator\FileExtensionValidator' OxidEsales\MediaLibrary\Validation\Service\UploadedFileValidatorChainInterface: @@ -15,8 +16,10 @@ services: public: true arguments: $fileValidators: + - '@OxidEsales\MediaLibrary\Validation\Validator\FileNameValidator' - '@OxidEsales\MediaLibrary\Validation\Validator\FileUploadStatusValidator' - '@OxidEsales\MediaLibrary\Validation\Validator\FileExtensionValidator' - OxidEsales\MediaLibrary\Validation\Validator\FileUploadStatusValidator: ~ OxidEsales\MediaLibrary\Validation\Validator\FileExtensionValidator: ~ + OxidEsales\MediaLibrary\Validation\Validator\FileNameValidator: ~ + OxidEsales\MediaLibrary\Validation\Validator\FileUploadStatusValidator: ~ diff --git a/tests/Unit/Validation/Service/FileNameValidatorChainTest.php b/tests/Unit/Validation/Service/FileNameValidatorChainTest.php index 6575e42..4aecc6f 100644 --- a/tests/Unit/Validation/Service/FileNameValidatorChainTest.php +++ b/tests/Unit/Validation/Service/FileNameValidatorChainTest.php @@ -18,7 +18,7 @@ use PHPUnit\Framework\TestCase; /** - * @covers \OxidEsales\MediaLibrary\Validation\Service\UploadedFileValidatorChain + * @covers \OxidEsales\MediaLibrary\Validation\Service\FileNameValidatorChain */ class FileNameValidatorChainTest extends TestCase { @@ -42,12 +42,12 @@ public function testExceptionOnValidatorException(): void { $fileName = uniqid(); - $validatorStub = $this->createMock(FilePathValidatorInterface::class); - $validatorStub->method('validateFile')->willThrowException(new ValidationFailedException()); + $validatorMock = $this->createMock(FilePathValidatorInterface::class); + $validatorMock->method('validateFile')->willThrowException(new ValidationFailedException()); $this->expectException(ValidationFailedException::class); - $sut = new FileNameValidatorChain([$validatorStub]); + $sut = new FileNameValidatorChain([$validatorMock]); $sut->validateFileName($fileName); } } diff --git a/tests/Unit/Validation/Validator/FileNameValidatorTest.php b/tests/Unit/Validation/Validator/FileNameValidatorTest.php new file mode 100644 index 0000000..8f89050 --- /dev/null +++ b/tests/Unit/Validation/Validator/FileNameValidatorTest.php @@ -0,0 +1,74 @@ +createConfiguredStub(FilePath::class, [ + 'getFileName' => uniqid() + ]); + + $sut = new FileNameValidator(); + $sut->validateFile($filePathStub); + + $this->addToAssertionCount(1); + } + + public static function goodFileNamesDataProvider(): \Generator + { + yield "regular file name" => [ + 'baseName' => uniqid(), + 'extension' => uniqid() + ]; + } + + public static function badFileNamesDataProvider(): \Generator + { + yield "empty" => [ + 'fileName' => '' + ]; + + yield "starts with dot" => [ + 'fileName' => '.' . uniqid() + ]; + } + + public static function forbiddenCharactersDataProvider(): \Generator + { + $characters = str_split(FileNameValidator::FORBIDDEN_CHARACTERS); + + foreach ($characters as $character) { + yield ['fileName' => uniqid() . $character . uniqid()]; + } + } + + /** + * @dataProvider badFileNamesDataProvider + * @dataProvider forbiddenCharactersDataProvider + */ + public function testFileNameEmptyThrowsException(string $fileName): void + { + $filePathStub = $this->createConfiguredStub(FilePath::class, [ + 'getFileName' => $fileName + ]); + + $this->expectException(ValidationFailedException::class); + + $sut = new FileNameValidator(); + $sut->validateFile($filePathStub); + } +}