Verified Commit f35b1b1e authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3420802 by kim.pepper, bkline, larowlan: [regression] file_save_upload...

Issue #3420802 by kim.pepper, bkline, larowlan: [regression] file_save_upload does not properly handle extensions

(cherry picked from commit 4caea1c8)
parent 5a1de8b7
Loading
Loading
Loading
Loading
Loading
+31 −19
Original line number Diff line number Diff line
@@ -341,31 +341,43 @@ protected function moveUploadedFile(UploadedFileInterface $uploadedFile, string
   *   The space delimited list of allowed file extensions.
   */
  protected function handleExtensionValidation(array &$validators): string {
    // Build a list of allowed extensions.
    if (isset($validators['FileExtension'])) {
      if (!isset($validators['FileExtension']['extensions'])) {
        // If 'FileExtension' is set and the list is empty then the caller wants
        // to allow any extension. In this case we have to remove the validator
        // or else it will reject all extensions.
        unset($validators['FileExtension']);
      }
    // Handle legacy extension validation.
    if (isset($validators['file_validate_extensions'])) {
      @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
      );
      // Empty string means all extensions are allowed so we should remove the
      // validator.
      if (\is_string($validators['file_validate_extensions']) && empty($validators['file_validate_extensions'])) {
        unset($validators['file_validate_extensions']);
        return '';
      }
    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);
      unset($validators['file_validate_extensions']);
      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().
    if (!isset($validators['FileExtension'])) {
      $validators['FileExtension'] = ['extensions' => self::DEFAULT_EXTENSIONS];
      return self::DEFAULT_EXTENSIONS;
    }

    // Check if we want to allow all extensions.
    if (!isset($validators['FileExtension']['extensions'])) {
      // If 'FileExtension' is set and the list is empty then the caller wants
      // to allow any extension. In this case we have to remove the validator
      // or else it will reject all extensions.
      unset($validators['FileExtension']);
      return '';
    }
    return $validators['FileExtension']['extensions'] ?? '';

    return $validators['FileExtension']['extensions'];
  }

  /**
+3 −0
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@ public function validate(FileInterface $file, array $validators): ConstraintViol
    foreach ($validators as $validator => $options) {
      if (function_exists($validator)) {
        @trigger_error('Support for file validation function ' . $validator . '() is deprecated in drupal:10.2.0 and will be removed in drupal:11.0.0. Use Symfony Constraints instead. See https://www.drupal.org/node/3363700', E_USER_DEPRECATED);
        if (!is_array($options)) {
          $options = [$options];
        }
        array_unshift($options, $file);
        // Call the validation function.
        // Options are a list of function args.
+70 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\file\Kernel;

use Drupal\Core\Messenger\MessengerInterface;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Tests file_save_upload().
 *
 * @group file
 * @group legacy
 */
class FileSaveUploadTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'file',
    'file_test',
    'file_validator_test',
    'user',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    \file_put_contents('test.bbb', 'test');

    parent::setUp();
    $request = new Request();
    $request->files->set('files', [
      'file' => new UploadedFile(
        path: 'test.bbb',
        originalName: 'test.bbb',
        mimeType: 'text/plain',
        error: \UPLOAD_ERR_OK,
        test: TRUE
      ),
    ]);

    $requestStack = new RequestStack();
    $requestStack->push($request);

    $this->container->set('request_stack', $requestStack);
  }

  /**
   * Tests file_save_upload() with empty extensions.
   */
  public function testFileSaveUploadEmptyExtensions(): void {
    // Allow all extensions.
    $validators = ['file_validate_extensions' => ''];
    $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');
    $files = file_save_upload('file', $validators);
    $this->assertCount(1, $files);
    $file = $files[0];
    // @todo work out why move_uploaded_file() is failing.
    $this->assertFalse($file);
    $messages = \Drupal::messenger()->messagesByType(MessengerInterface::TYPE_ERROR);
    $this->assertNotEmpty($messages);
    $this->assertEquals('File upload error. Could not move uploaded file.', $messages[0]);
  }

}