SecurityFileUploadEventSubscriberTest.php

Same filename and directory in other branches
  1. 9 core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php
  2. 10 core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php

Namespace

Drupal\Tests\system\Unit\Event

File

core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\system\Unit\Event;

use Drupal\Core\File\Event\FileUploadSanitizeNameEvent;
use Drupal\system\EventSubscriber\SecurityFileUploadEventSubscriber;
use Drupal\Tests\UnitTestCase;

/**
 * SecurityFileUploadEventSubscriber tests.
 *
 * @group system
 * @coversDefaultClass \Drupal\system\EventSubscriber\SecurityFileUploadEventSubscriber
 */
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) : void {
    // 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 static 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 even if some extensions are allowed' => [
        'foo' . chr(0) . '.html' . chr(0),
        'txt',
        'foo.html',
      ],
      'dot files are renamed' => [
        '.git',
        '',
        'git',
      ],
      'htaccess files are renamed even if allowed' => [
        '.htaccess',
        'htaccess txt',
        'htaccess',
      ],
      '.phtml extension allowed with .phtml file' => [
        'foo.phtml',
        'phtml',
        'foo.phtml',
      ],
      '.phtml, .txt extension allowed with .phtml file' => [
        'foo.phtml',
        'phtml txt',
        'foo.phtml_.txt',
        'foo.phtml',
      ],
      'All extensions allowed with .phtml file' => [
        'foo.phtml',
        '',
        'foo.phtml_.txt',
        'foo.phtml',
      ],
      'dot files are renamed even if allowed and not in security list' => [
        '.git',
        'git',
        'git',
      ],
    ];
  }
  
  /**
   * 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) : void {
    $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->assertFalse($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->assertFalse($event->isSecurityRename());
  }
  
  /**
   * Provides data for testSanitizeNameNoMunge().
   *
   * @return array
   *   Arrays with original name and allowed extensions.
   */
  public static function provideFilenamesNoMunge() {
    return [
      // The following filename would be rejected by 'FileExtension' constraint
      // and therefore remains unchanged.
'.php is not munged when it would be rejected' => [
        'foo.php.php',
        'jpg',
      ],
      'extension less files are not munged when they would be rejected' => [
        'foo',
        'jpg',
      ],
    ];
  }

}

Classes

Title Deprecated Summary
SecurityFileUploadEventSubscriberTest SecurityFileUploadEventSubscriber tests.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.