[regression] file_save_upload does not properly handle extensions

Created on 12 February 2024, 9 months ago
Updated 16 March 2024, 8 months ago

Problem/Motivation

On upgrading to Drupal 10.2.2 we found that file_save_upload() is now broken. According to its documentation:

Parameters ... array $validators ... To allow all extensions, you must explicitly set this array to ['file_validate_extensions' => ''].

That has worked fine for years. With the upgrade, it blows up. In the debugger we can see that the failure happens in the new FileValidator::validate() method, where an attempt to invoke array_unshift() on the empty string raises a TypeError exception.

Steps to reproduce

  1. Install the latest Drupal 10.2.x
  2. Invoke file_save_upload() with the $validators parameter set as instructed in the documentation quoted above
  3. note the exception thrown

Proposed resolution

Either fix the code to match the documentation, or revise the documentation to match the modified behavior of the software.

User interface changes

None

API changes

The API behavior will once again match the documentation.

Data model changes

N/A

πŸ› Bug report
Status

Fixed

Version

10.2 ✨

Component
File systemΒ  β†’

Last updated about 12 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • Issue created by @bkline
  • Status changed to Postponed: needs info 9 months ago
  • Provide the exception message. You alluded to it but left it out. We may need a stack trace too.

  • Also provide the custom code that exhibits the bug.

  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

    Sorry. I assumed when I gave the exception class (TypeError) and the method where array_unshift() is invoked (FileValidator::validate()) you'd have enough information. It's no surprise that array_unshift() is unhappy when the first argument isn't an array. πŸ˜‰

    Here's the exception:

    [Mon Feb 12 06:57:09.773233 2024] [php:notice] [pid 788] [client 192.168.65.1:24832] Uncaught PHP Exception TypeError: "array_unshift(): Argument #1 ($array) must be of type array, string given" at /var/www/web/core/modules/file/src/Validation/FileValidator.php line 49, referer: http://ebms.localhost:8081/articles/import/27530

    In this case, the apache logs don't get the full stack trace, so I've captured the call stack from the debugger. This is right before array_unshift() is called with $options set to an empty string:

    and here is the call stack immediately after the exception is thrown:

    Here's the custom code calling file_save_upload():

              $validators = ['file_validate_extensions' => ''];
              $file = file_save_upload('file', $validators, FALSE, 0);
    

    I'm not 100% certain you'll be able to open this GitHub URL, as our client has recently added an additional login requirement, which sometimes blocks access and sometimes doesn't (I haven't yet completely figured out the pattern):

    https://github.com/NCIOCPL/ebms/blob/main/web/modules/custom/ebms_import...

  • Status changed to Needs work 9 months ago
  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    kim.pepper β†’ made their first commit to this issue’s fork.

  • Assigned to kim.pepper
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Writing a test to reproduce this.

  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

    Let me know if you're not able to get to the GitHub link I provided, and I'll come up with another way to provide you with our custom code.

  • Merge request !6588#3420802 FileValidator BC break β†’ (Open) created by kim.pepper
  • Status changed to Needs review 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Added a fix to check if the validator $options is an array in \Drupal\file\Validation\FileValidator::validate().

    We can do a test only build to make sure it's fixed.

  • Pipeline finished with Success
    9 months ago
    Total: 644s
    #94371
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    So the default MR pipeline passes and the test-only pipeline fails, so I think we have a fix.

  • Pipeline finished with Success
    9 months ago
    Total: 475s
    #94379
  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

    Using your patch to avoid blowing up on the array_unshift() call, I'm now getting "Only files with the following extensions are allowed: jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp." I see that your test uses a file with a name ending in ".txt" which is one of the extensions on the default list, whereas my test with an extension not on the list fails, meaning the advice in the documentation no longer works (that is, it doesn't achieve the goal of allowing any filename extension). What happens if your test uses a filename like "aaa.bbb"?

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Looks like there is more than one issue. I think this should fix the issue, but we need to add a test for the changed logic in \Drupal\file\Upload\FileUploadHandler::handleExtensionValidation(). Not sure if that should be a separate issue?

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia
  • Pipeline finished with Success
    9 months ago
    Total: 634s
    #94420
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    I'd really like to sort this out in πŸ“Œ Simplify how 'allow all extensions' file upload validation works Active

  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

    I'll test in the morning (EST).

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    I added a test for \Drupal\file\Upload\FileUploadHandler::handleExtensionValidation() but needed to use reflection in order to get full test coverage of a protected method.

  • Pipeline finished with Failed
    9 months ago
    Total: 849s
    #94537
  • Pipeline finished with Canceled
    9 months ago
    Total: 207s
    #94559
  • Pipeline finished with Success
    9 months ago
    Total: 736s
    #94565
  • πŸ‡ΊπŸ‡ΈUnited States bkline Virginia

    Confirming that it works as documented again with the patch.

    We understand that eventually (that is, before we get to D11) we need to replace

              $validators = ['file_validate_extensions' => ''];
    

    with

              $validators = ['FileExtension' => []];
    

    (or whatever the documentation for the replacement for the soon-to-be-deprecated file_save_upload() function tells us to use).

    Thanks!

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Bumping this to major, given this effectively stops file uploads that 'allow all' extensions.

  • Status changed to Needs work 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Left some questions on the MR, thanks for jumping on this - and for testing @bkline@rksystems.com

  • Pipeline finished with Canceled
    9 months ago
    Total: 473s
    #99037
  • Pipeline finished with Canceled
    9 months ago
    Total: 101s
    #99041
  • Pipeline finished with Success
    9 months ago
    #99042
  • Status changed to Needs review 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Tests are green and all feedback has been addressed.

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia
  • Status changed to Needs work 9 months ago
  • The Needs Review Queue Bot β†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • Status changed to Needs review 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Review bot is complaining about a mysql file not included in the MR so disabling.

  • πŸ‡«πŸ‡·France nod_ Lille

    yeah sometimes the bot picks up some stuff from someone's test issue or something and it causes problems. cleaned it up.

  • Status changed to Needs work 9 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Appears all feedback has been addressed

    But can MR be updated for 11.x

  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    It's a bug in 10.2.x so I assumed it should be against that?

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Wouldn't this issue appear in 11.x and 10.3 though?

  • Pipeline finished with Success
    9 months ago
    Total: 594s
    #104506
  • Pipeline finished with Success
    9 months ago
    Total: 504s
    #104508
  • Pipeline finished with Canceled
    9 months ago
    Total: 224s
    #104517
  • Status changed to Needs review 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Yeah makes sense.

    I first tried to change the target branch but that meant I needed to rebase on every change from 10.2.x to 11.x 😬

    I then decided it would be better to create a new branch off 11.x and cherry pick the commits from 3420802-filesaveupload-broken-by onto 3420802-11.x-filesaveupload-broken. Somehow it didn't get the change in FileValidator so I manually made that change in another commit.

    I'm sure there is an easier way to do this. πŸ˜…

  • Pipeline finished with Success
    9 months ago
    Total: 487s
    #104522
  • Status changed to RTBC 9 months ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Thanks for the quick response!

  • πŸ‡¬πŸ‡§United Kingdom alexpott πŸ‡ͺπŸ‡ΊπŸŒ

    Committed and pushed 4caea1c8e7 to 11.x and 7fa87dc411 to 10.3.x and f35b1b1e79 to 10.2.x. Thanks!

    It's a shame that the new constraint supports an empty extension list. Also it feels even less explicit (and therefore easier to mistakenly dod) than the old way. Can we file a follow-up to deprecate configuring \Drupal\file\Plugin\Validation\Constraint\FileExtensionConstraint without extensions and implement a whole other constraint for the purpose of allowing any files. Not you can not (at least in past) configure a file field via the UI to accept any extension. The constraint should be called InsecureFileUpload or something that will make you think a lot before ever using.

  • Status changed to Fixed 9 months ago
  • πŸ‡¦πŸ‡ΊAustralia kim.pepper πŸ„β€β™‚οΈπŸ‡¦πŸ‡ΊSydney, Australia

    Thanks @alexpott. I started looking at how we could improve "unsafe mode" in πŸ“Œ Simplify how 'allow all extensions' file upload validation works Active but didn't get too far.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024