diff -u b/core/modules/file/file.module b/core/modules/file/file.module --- b/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -25,6 +25,8 @@ use Drupal\Core\Template\Attribute; use Symfony\Component\Mime\MimeTypeGuesserInterface; +// cspell:ignore btxt + /** * The regex pattern used when checking for insecure file types. */ @@ -1003,7 +1005,7 @@ // If the file will be rejected anyway due to a disallowed extension, it // should not be renamed; rather, we'll let file_validate_extensions() // reject it below. - if (!isset($validators['file_validate_extensions']) || (preg_match('/(^|\s)txt(\s|$)/', $extensions) && empty(file_validate_extensions($file, $extensions)))) { + if (!isset($validators['file_validate_extensions']) || (preg_match('/\btxt\b/', $extensions) && empty(file_validate_extensions($file, $extensions)))) { $file->setMimeType('text/plain'); $filename = $file->getFilename(); if (substr($filename, -4) != '.txt') { diff -u b/core/modules/file/file.post_update.php b/core/modules/file/file.post_update.php --- b/core/modules/file/file.post_update.php +++ b/core/modules/file/file.post_update.php @@ -10,7 +10,8 @@ use Drupal\file\Plugin\Field\FieldType\FileItem; /** - * Adds txt to allowed extensions for all fields that allow uploads of insecure files. + * Adds txt to allowed extensions for all fields that allow uploads of insecure + * files. */ function file_post_update_update_allowed_file_extensions_if_insecure(&$sandbox = NULL) { if (\Drupal::config('system.file')->get('allow_insecure_uploads')) { @@ -20,7 +21,7 @@ $updater = function (FieldConfig $field) { // Determine if this is field uses a item definition that extends FileItem. if (is_subclass_of($field->getItemDefinition()->getClass(), FileItem::class)) { - $allowed_extensions_string = $field->getSetting('file_extensions'); + $allowed_extensions_string = trim($field->getSetting('file_extensions')); $allowed_extensions = array_filter(explode(' ', $allowed_extensions_string)); if (in_array('txt', $allowed_extensions, TRUE)) { // .txt is specifically allowed there's nothing to do. @@ -29,7 +30,7 @@ foreach ($allowed_extensions as $extension) { if (preg_match(FILE_INSECURE_EXTENSION_REGEX, 'test.' . $extension)) { // Add the txt extension to the list of allowed extensions if an - // insecure is allowed. + // insecure (one|extension) is allowed. $allowed_extensions_string .= ' txt'; $field->setSetting('file_extensions', $allowed_extensions_string); return TRUE; diff -u b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php --- b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php +++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php @@ -236,11 +236,17 @@ // If insecure uploads are not allowed then error if txt is not an allowed // extension. - if (!in_array('txt', $extension_array) && !\Drupal::config('system.file')->get('allow_insecure_uploads')) { - foreach ($extension_array as $extension) { - if (preg_match(FILE_INSECURE_EXTENSION_REGEX, 'test.' . $extension)) { - $form_state->setError($element, t('Add %txt_extension to the list of allowed extensions to securely upload files with a %extension extension. The %txt_extension extension will then be added automatically.', ['%extension' => $extension, '%txt_extension' => 'txt'])); + if (!in_array('txt', $extension_array, TRUE) && !\Drupal::config('system.file')->get('allow_insecure_uploads')) { + $unique_extensions = []; + // Filter out definitions that are not intended to be placed by the UI. + $unique_extensions = array_filter($extension_array, function (string $extension) { + if (!in_array($extension, $unique_extensions) && preg_match(FILE_INSECURE_EXTENSION_REGEX, 'test.' . $extension)) { + return $extension; } + }); + if (!empty($unique_extensions)) { + $unique_extensions = implode(' or ', $unique_extensions); + $form_state->setError($element, t('Add %txt_extension to the list of allowed extensions to securely upload files with a %extension extension. The %txt_extension extension will then be added automatically.', ['%extension' => $unique_extensions, '%txt_extension' => 'txt'])); } } } diff -u b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php --- b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php +++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php @@ -32,6 +32,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\HttpException; +// cspell:ignore btxt + /** * File upload resource. * @@ -490,7 +492,7 @@ $file->setFilename($filename); $passes_validation = empty(file_validate_extensions($file, $validators['file_validate_extensions'][0])); // Only allow upload and rename to .txt if .txt files are allowed. - $passes_validation = $passes_validation && preg_match('/(^|\s)txt(\s|$)/', $validators['file_validate_extensions'][0]); + $passes_validation = $passes_validation && preg_match('/\btxt\b/', $validators['file_validate_extensions'][0]); } else { // All file extensions are allowed. diff -u b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php --- b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php +++ b/core/modules/jsonapi/src/Controller/TemporaryJsonapiFileFieldUploader.php @@ -27,6 +27,8 @@ use Symfony\Component\Mime\MimeTypeGuesserInterface; use Symfony\Component\Validator\ConstraintViolation; +// cspell:ignore btxt + /** * Reads data from an upload stream and creates a corresponding file entity. * @@ -408,7 +410,7 @@ $file->setFilename($filename); $passes_validation = empty(file_validate_extensions($file, $validators['file_validate_extensions'][0])); // Only allow upload and rename to .txt if .txt files are allowed. - $passes_validation = $passes_validation && preg_match('/(^|\s)txt(\s|$)/', $validators['file_validate_extensions'][0]); + $passes_validation = $passes_validation && preg_match('/\btxt\b/', $validators['file_validate_extensions'][0]); } else { // All file extensions are allowed.