From 02c75e7160e691abadd4fd7def6fc0c86fedb6d6 Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Thu, 26 Oct 2023 07:30:41 +1000 Subject: [PATCH] Issue #3394406 by kim.pepper, larowlan, bnjmnm, smustgrave: FileUploadHandler::handleExtensionValidation does not have fallback for sites still using file_validate_extensions --- .../file/src/Upload/FileUploadHandler.php | 9 +++ .../file_validator_test.services.yml | 4 + .../FileSanitizationEventSubscriber.php | 44 +++++++++++ .../src/Kernel/FileUploadHandlerTest.php | 73 +++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 core/modules/file/tests/modules/file_validator_test/src/EventSubscriber/FileSanitizationEventSubscriber.php create mode 100644 core/modules/file/tests/src/Kernel/FileUploadHandlerTest.php diff --git a/core/modules/file/src/Upload/FileUploadHandler.php b/core/modules/file/src/Upload/FileUploadHandler.php index bbc95e0fef6b..a2c07911fdb8 100644 --- a/core/modules/file/src/Upload/FileUploadHandler.php +++ b/core/modules/file/src/Upload/FileUploadHandler.php @@ -376,10 +376,19 @@ protected function handleExtensionValidation(array &$validators): string { } } else { + if (!empty($validators['file_validate_extensions'][0])) { + // The deprecated 'file_validate_extensions' has configuration, so that + // should be used. + $validators['FileExtension']['extensions'] = $validators['file_validate_extensions'][0]; + @trigger_error('\'file_validate_extensions\' is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use the \'FileExtension\' constraint instead. See https://www.drupal.org/node/3363700', E_USER_DEPRECATED); + return $validators['FileExtension']['extensions']; + } + // No validator was provided, so add one using the default list. // Build a default non-munged safe list for // \Drupal\system\EventSubscriber\SecurityFileUploadEventSubscriber::sanitizeName(). $validators['FileExtension'] = ['extensions' => self::DEFAULT_EXTENSIONS]; + } return $validators['FileExtension']['extensions'] ?? ''; } diff --git a/core/modules/file/tests/modules/file_validator_test/file_validator_test.services.yml b/core/modules/file/tests/modules/file_validator_test/file_validator_test.services.yml index bbf16998a255..52a45d1fb1be 100644 --- a/core/modules/file/tests/modules/file_validator_test/file_validator_test.services.yml +++ b/core/modules/file/tests/modules/file_validator_test/file_validator_test.services.yml @@ -3,3 +3,7 @@ services: class: Drupal\file_validator_test\EventSubscriber\FileValidationTestSubscriber tags: - { name: event_subscriber } + file_validation_sanitization_subscriber: + class: Drupal\file_validator_test\EventSubscriber\FileSanitizationEventSubscriber + tags: + - {name: event_subscriber} diff --git a/core/modules/file/tests/modules/file_validator_test/src/EventSubscriber/FileSanitizationEventSubscriber.php b/core/modules/file/tests/modules/file_validator_test/src/EventSubscriber/FileSanitizationEventSubscriber.php new file mode 100644 index 000000000000..f7774d991f46 --- /dev/null +++ b/core/modules/file/tests/modules/file_validator_test/src/EventSubscriber/FileSanitizationEventSubscriber.php @@ -0,0 +1,44 @@ +<?php + +namespace Drupal\file_validator_test\EventSubscriber; + +use Drupal\Core\File\Event\FileUploadSanitizeNameEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Provides a file sanitization listener for file upload tests. + */ +class FileSanitizationEventSubscriber implements EventSubscriberInterface { + + /** + * The allowed extensions. + * + * @var string[] + */ + protected array $allowedExtensions = []; + + /** + * Handles the file sanitization event. + */ + public function onFileSanitization(FileUploadSanitizeNameEvent $event) { + $this->allowedExtensions = $event->getAllowedExtensions(); + } + + /** + * Gets the allowed extensions. + * + * @return string[] + * The allowed extensions. + */ + public function getAllowedExtensions(): array { + return $this->allowedExtensions; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [FileUploadSanitizeNameEvent::class => 'onFileSanitization']; + } + +} diff --git a/core/modules/file/tests/src/Kernel/FileUploadHandlerTest.php b/core/modules/file/tests/src/Kernel/FileUploadHandlerTest.php new file mode 100644 index 000000000000..bdc5ccbcfdb0 --- /dev/null +++ b/core/modules/file/tests/src/Kernel/FileUploadHandlerTest.php @@ -0,0 +1,73 @@ +<?php + +namespace Drupal\Tests\file\Kernel; + +use Drupal\file\Upload\FileUploadHandler; +use Drupal\file\Upload\UploadedFileInterface; +use Drupal\KernelTests\KernelTestBase; +use Symfony\Component\Mime\MimeTypeGuesserInterface; + +/** + * Tests the file upload handler. + * + * @group file + */ +class FileUploadHandlerTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['file', 'file_validator_test']; + + /** + * The file upload handler under test. + */ + protected FileUploadHandler $fileUploadHandler; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + $this->fileUploadHandler = $this->container->get('file.upload_handler'); + } + + /** + * Tests the legacy extension support. + * + * @group legacy + */ + public function testLegacyExtensions(): void { + $filename = $this->randomMachineName() . '.txt'; + $uploadedFile = $this->createMock(UploadedFileInterface::class); + $uploadedFile->expects($this->once()) + ->method('getClientOriginalName') + ->willReturn($filename); + $uploadedFile->expects($this->once())->method('isValid')->willReturn(TRUE); + + // Throw an exception in mimeTypeGuesser to return early from the method. + $mimeTypeGuesser = $this->createMock(MimeTypeGuesserInterface::class); + $mimeTypeGuesser->expects($this->once())->method('guessMimeType') + ->willThrowException(new \RuntimeException('Expected exception')); + + $fileUploadHandler = new FileUploadHandler( + fileSystem: $this->container->get('file_system'), + entityTypeManager: $this->container->get('entity_type.manager'), + streamWrapperManager: $this->container->get('stream_wrapper_manager'), + eventDispatcher: $this->container->get('event_dispatcher'), + mimeTypeGuesser: $mimeTypeGuesser, + currentUser: $this->container->get('current_user'), + requestStack: $this->container->get('request_stack'), + fileRepository: $this->container->get('file.repository'), + file_validator: $this->container->get('file.validator'), + ); + + $this->expectException(\Exception::class); + $this->expectDeprecation('\'file_validate_extensions\' is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use the \'FileExtension\' constraint instead. See https://www.drupal.org/node/3363700'); + $fileUploadHandler->handleFileUpload($uploadedFile, ['file_validate_extensions' => ['txt']]); + + $subscriber = $this->container->get('file_validation_sanitization_subscriber'); + $this->assertEquals(['txt'], $subscriber->getAllowedExtensions()); + } + +} -- GitLab