public function FileUploadResourceTestBase::testFileUploadMaliciousExtension in Drupal 10
Same name and namespace in other branches
- 8 core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php \Drupal\Tests\rest\Functional\FileUploadResourceTestBase::testFileUploadMaliciousExtension()
- 9 core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php \Drupal\Tests\rest\Functional\FileUploadResourceTestBase::testFileUploadMaliciousExtension()
Tests using the file upload POST route with malicious extensions.
File
- core/modules/ rest/ tests/ src/ Functional/ FileUploadResourceTestBase.php, line 502 
Class
- FileUploadResourceTestBase
- Tests binary data file upload route.
Namespace
Drupal\Tests\rest\FunctionalCode
public function testFileUploadMaliciousExtension() {
  $this
    ->initAuthentication();
  $this
    ->provisionResource([
    static::$format,
  ], static::$auth ? [
    static::$auth,
  ] : [], [
    'POST',
  ]);
  // Allow all file uploads but system.file::allow_insecure_uploads is set to
  // FALSE.
  $this->field
    ->setSetting('file_extensions', '')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  $this
    ->setUpAuthorization('POST');
  $uri = Url::fromUri('base:' . static::$postUri);
  $php_string = '<?php print "Drupal"; ?>';
  // Test using a masked exploit file.
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example.php"',
  ]);
  // The filename is not munged because .txt is added and it is a known
  // extension to apache.
  $expected = $this
    ->getExpectedNormalizedEntity(1, 'example.php_.txt', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example.php_.txt');
  // Add .php and .txt as allowed extensions. Since 'allow_insecure_uploads'
  // is FALSE, .php files should be renamed to have a .txt extension.
  $this->field
    ->setSetting('file_extensions', 'php txt')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_2.php"',
  ]);
  $expected = $this
    ->getExpectedNormalizedEntity(2, 'example_2.php_.txt', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_2.php_.txt');
  $this
    ->assertFileDoesNotExist('public://foobar/example_2.php');
  // Allow .doc file uploads and ensure even a mis-configured apache will not
  // fallback to php because the filename will be munged.
  $this->field
    ->setSetting('file_extensions', 'doc')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  // Test using a masked exploit file.
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_3.php.doc"',
  ]);
  // The filename is munged.
  $expected = $this
    ->getExpectedNormalizedEntity(3, 'example_3.php_.doc', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  // The file mime should be 'application/msword'.
  $expected['filemime'][0]['value'] = 'application/msword';
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_3.php_.doc');
  $this
    ->assertFileDoesNotExist('public://foobar/example_3.php.doc');
  // Test that a dangerous extension such as .php is munged even if it is in
  // the list of allowed extensions.
  $this->field
    ->setSetting('file_extensions', 'doc php')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  // Test using a masked exploit file.
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_4.php.doc"',
  ]);
  // The filename is munged.
  $expected = $this
    ->getExpectedNormalizedEntity(4, 'example_4.php_.doc', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  // The file mime should be 'application/msword'.
  $expected['filemime'][0]['value'] = 'application/msword';
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_4.php_.doc');
  $this
    ->assertFileDoesNotExist('public://foobar/example_4.php.doc');
  // Dangerous extensions are munged even when all extensions are allowed.
  $this->field
    ->setSetting('file_extensions', '')
    ->save();
  $this
    ->rebuildAll();
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_5.php.png"',
  ]);
  $expected = $this
    ->getExpectedNormalizedEntity(5, 'example_5.php_.png', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  // The file mime should still see this as a PNG image.
  $expected['filemime'][0]['value'] = 'image/png';
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_5.php_.png');
  // Dangerous extensions are munged if is renamed to end in .txt.
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_6.cgi.png.txt"',
  ]);
  $expected = $this
    ->getExpectedNormalizedEntity(6, 'example_6.cgi_.png_.txt', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  // The file mime should also now be text.
  $expected['filemime'][0]['value'] = 'text/plain';
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_6.cgi_.png_.txt');
  // Add .php as an allowed extension without .txt. Since insecure uploads are
  // not allowed, .php files will be rejected.
  $this->field
    ->setSetting('file_extensions', 'php')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_7.php"',
  ]);
  $this
    ->assertResourceErrorResponse(422, "Unprocessable Entity: file validation failed.\nFor security reasons, your upload has been rejected.", $response);
  // Make sure that no file was saved.
  $this
    ->assertFileDoesNotExist('public://foobar/example_7.php');
  $this
    ->assertFileDoesNotExist('public://foobar/example_7.php.txt');
  // Now allow insecure uploads.
  \Drupal::configFactory()
    ->getEditable('system.file')
    ->set('allow_insecure_uploads', TRUE)
    ->save();
  // Allow all file uploads. This is very insecure.
  $this->field
    ->setSetting('file_extensions', '')
    ->save();
  $this
    ->refreshTestStateAfterRestConfigChange();
  $response = $this
    ->fileRequest($uri, $php_string, [
    'Content-Disposition' => 'filename="example_7.php"',
  ]);
  $expected = $this
    ->getExpectedNormalizedEntity(7, 'example_7.php', TRUE);
  // Override the expected filesize.
  $expected['filesize'][0]['value'] = strlen($php_string);
  // The file mime should also now be PHP.
  $expected['filemime'][0]['value'] = 'application/x-httpd-php';
  $this
    ->assertResponseData($expected, $response);
  $this
    ->assertFileExists('public://foobar/example_7.php');
}