You are here

class SecurityFileUploadEventSubscriberTest in Drupal 9

SecurityFileUploadEventSubscriber tests.

@group system @coversDefaultClass \Drupal\system\EventSubscriber\SecurityFileUploadEventSubscriber

Hierarchy

Expanded class hierarchy of SecurityFileUploadEventSubscriberTest

File

core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php, line 15

Namespace

Drupal\Tests\system\Unit\Event
View source
class SecurityFileUploadEventSubscriberTest extends UnitTestCase {

  /**
   * Tests file name sanitization.
   *
   * @param string $filename
   *   The original filename.
   * @param string $allowed_extensions
   *   The allowed extensions.
   * @param string $expected_filename
   *   The expected filename if 'allow_insecure_uploads' is set to FALSE.
   * @param string|null $expected_filename_with_insecure_uploads
   *   The expected filename if 'allow_insecure_uploads' is set to TRUE.
   *
   * @dataProvider provideFilenames
   *
   * @covers ::sanitizeName
   */
  public function testSanitizeName(string $filename, string $allowed_extensions, string $expected_filename, string $expected_filename_with_insecure_uploads = NULL) {

    // Configure insecure uploads to be renamed.
    $config_factory = $this
      ->getConfigFactoryStub([
      'system.file' => [
        'allow_insecure_uploads' => FALSE,
      ],
    ]);
    $subscriber = new SecurityFileUploadEventSubscriber($config_factory);
    $event = new FileUploadSanitizeNameEvent($filename, $allowed_extensions);
    $subscriber
      ->sanitizeName($event);

    // Check the results of the configured sanitization.
    $this
      ->assertSame($expected_filename, $event
      ->getFilename());
    $this
      ->assertSame($expected_filename !== $filename, $event
      ->isSecurityRename());

    // Rerun the event allowing insecure uploads.
    $config_factory = $this
      ->getConfigFactoryStub([
      'system.file' => [
        'allow_insecure_uploads' => TRUE,
      ],
    ]);
    $subscriber = new SecurityFileUploadEventSubscriber($config_factory);
    $event = new FileUploadSanitizeNameEvent($filename, $allowed_extensions);
    $subscriber
      ->sanitizeName($event);

    // Check the results of the configured sanitization.
    $expected_filename_with_insecure_uploads = $expected_filename_with_insecure_uploads ?? $expected_filename;
    $this
      ->assertSame($expected_filename_with_insecure_uploads, $event
      ->getFilename());
    $this
      ->assertSame($expected_filename_with_insecure_uploads !== $filename, $event
      ->isSecurityRename());
  }

  /**
   * Provides data for testSanitizeName().
   *
   * @return array
   *   Arrays with original name, allowed extensions, expected name and
   *   (optional) expected name 'allow_insecure_uploads' is set to TRUE.
   */
  public function provideFilenames() {
    return [
      'All extensions allowed filename not munged' => [
        'foo.txt',
        '',
        'foo.txt',
      ],
      'All extensions allowed with .php file' => [
        'foo.php',
        '',
        'foo.php_.txt',
        'foo.php',
      ],
      'All extensions allowed with .pHp file' => [
        'foo.pHp',
        '',
        'foo.pHp_.txt',
        'foo.pHp',
      ],
      'All extensions allowed with .PHP file' => [
        'foo.PHP',
        '',
        'foo.PHP_.txt',
        'foo.PHP',
      ],
      '.php extension allowed with .php file' => [
        'foo.php',
        'php',
        'foo.php',
        'foo.php',
      ],
      '.PhP extension allowed with .php file' => [
        'foo.php',
        'PhP',
        'foo.php',
        'foo.php',
      ],
      '.php, .txt extension allowed with .php file' => [
        'foo.php',
        'php txt',
        'foo.php_.txt',
        'foo.php',
      ],
      '.PhP, .tXt extension allowed with .php file' => [
        'foo.php',
        'PhP tXt',
        'foo.php_.txt',
        'foo.php',
      ],
      'no extension produces no errors' => [
        'foo',
        '',
        'foo',
      ],
      'filename is munged' => [
        'foo.phar.png.php.jpg',
        'jpg png',
        'foo.phar_.png_.php_.jpg',
      ],
      'filename is munged regardless of case' => [
        'FOO.pHAR.PNG.PhP.jpg',
        'jpg png',
        'FOO.pHAR_.PNG_.PhP_.jpg',
      ],
      'null bytes are removed' => [
        'foo' . chr(0) . '.txt' . chr(0),
        '',
        'foo.txt',
      ],
      'dot files are renamed' => [
        '.htaccess',
        '',
        'htaccess',
      ],
    ];
  }

  /**
   * Tests file name sanitization without file munging.
   *
   * @param string $filename
   *   The original filename.
   * @param string $allowed_extensions
   *   The allowed extensions.
   *
   * @dataProvider provideFilenamesNoMunge
   *
   * @covers ::sanitizeName
   */
  public function testSanitizeNameNoMunge(string $filename, string $allowed_extensions) {
    $config_factory = $this
      ->getConfigFactoryStub([
      'system.file' => [
        'allow_insecure_uploads' => FALSE,
      ],
    ]);
    $subscriber = new SecurityFileUploadEventSubscriber($config_factory);
    $event = new FileUploadSanitizeNameEvent($filename, $allowed_extensions);
    $subscriber
      ->sanitizeName($event);

    // Check the results of the configured sanitization.
    $this
      ->assertSame($filename, $event
      ->getFilename());
    $this
      ->assertSame(FALSE, $event
      ->isSecurityRename());
    $config_factory = $this
      ->getConfigFactoryStub([
      'system.file' => [
        'allow_insecure_uploads' => TRUE,
      ],
    ]);
    $event = new FileUploadSanitizeNameEvent($filename, $allowed_extensions);
    $subscriber = new SecurityFileUploadEventSubscriber($config_factory);
    $subscriber
      ->sanitizeName($event);

    // Check the results of the configured sanitization.
    $this
      ->assertSame($filename, $event
      ->getFilename());
    $this
      ->assertSame(FALSE, $event
      ->isSecurityRename());
  }

  /**
   * Provides data for testSanitizeNameNoMunge().
   *
   * @return array
   *   Arrays with original name and allowed extensions.
   */
  public function provideFilenamesNoMunge() {
    return [
      // The following filename would be rejected by file_validate_extension()
      // and therefore remains unchanged.
      '.php is not munged when it would be rejected' => [
        'foo.php.php',
        'jpg',
      ],
      '.php is not munged when it would be rejected and filename contains null byte character' => [
        'foo.' . chr(0) . 'php.php',
        'jpg',
      ],
      'extension less files are not munged when they would be rejected' => [
        'foo',
        'jpg',
      ],
      'dot files are not munged when they would be rejected' => [
        '.htaccess',
        'jpg png',
      ],
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpUnitWarnings::$deprecationWarnings private static property Deprecation warnings from PHPUnit to raise with @trigger_error().
PhpUnitWarnings::addWarning public function Converts PHPUnit deprecation warnings to E_USER_DEPRECATED.
SecurityFileUploadEventSubscriberTest::provideFilenames public function Provides data for testSanitizeName().
SecurityFileUploadEventSubscriberTest::provideFilenamesNoMunge public function Provides data for testSanitizeNameNoMunge().
SecurityFileUploadEventSubscriberTest::testSanitizeName public function Tests file name sanitization.
SecurityFileUploadEventSubscriberTest::testSanitizeNameNoMunge public function Tests file name sanitization without file munging.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals Deprecated protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.
UnitTestCase::setUp protected function 308
UnitTestCase::setUpBeforeClass public static function