Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(multipart): add max_length to secure_filename() #2421

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Krishn1412
Copy link

  1. adding max_length parameter which truncates the file but includes the ext.
  2. This is presently set to None but can be set 255 characters.

1) adding max_length parameter which truncates the file but includes the ext.
2) This is presently set to None but can be set 255 characters.
Copy link

codecov bot commented Dec 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (7621942) to head (a323dea).

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #2421   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           64        64           
  Lines         7728      7737    +9     
  Branches      1071      1073    +2     
=========================================
+ Hits          7728      7737    +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@vytas7 vytas7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @Krishn1412!

In order to speed up the review process though, it is usually more efficient if you make sure that at least tox passes locally for you first. Thanks!

falcon/util/misc.py Outdated Show resolved Hide resolved
@vytas7 vytas7 changed the title Adding max_length in secure_filename feat(multipart): add max_length to secure_filename() Dec 15, 2024
Copy link
Member

@vytas7 vytas7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provided implementation suggestions in-line.

Also, you still need to make all the tox tests pass. To that end, you'll also need to add new unit tests verifying the proposed behaviour.

the file extension. Defaults to ``None`` for backward compatibility.

.. note::
Starting from Falcon 5.0, this will default to 255 characters.
Copy link
Member

@vytas7 vytas7 Dec 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest a smaller default value. On some systems, e.g., Windows, the max possible path length is around 255-260, so likely we will want to make the file name part of the path shorter than that.

Maybe 64 could be a sane default?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

64 may be a bit small as default, but 96/128 is likely ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I think 64 ought to be enough for the basename, since secure_filename() is mangling the name in any case, so there is no guarantee to get exactly the same string as submitted in the form anyway. But 96 works for me as well.

falcon/util/misc.py Outdated Show resolved Hide resolved
I have changed the logic as suggested in the review.

For the unit tests, I have made direct function call to test the logic.
CaselIT
CaselIT previously approved these changes Dec 16, 2024
Copy link
Member

@CaselIT CaselIT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other than the max length for v5 the rest looks fine, thanks!

falcon/util/misc.py Show resolved Hide resolved
Copy link
Member

@vytas7 vytas7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the algorithm itself looks good now!

It seems that you forgot to actually forgot to wire the new option into the actual implementation of the BodyPart.secure_filename property though.

See also other comments inline.

@@ -592,6 +592,15 @@ class MultipartParseOptions:
If the body part headers size exceeds this value, an instance of
:class:`.MultipartParseError` will be raised.
"""
max_filename_length: Optional[int]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new length is not passed to the actual invocation of secure_filename() when referencing the corresponding part property.

Also add tests (make sure to parametrize on both WSGI/ASGI) for actually modifying this parsing option, and verifying that it is respected when referencing the said property.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add a newsfragment for this improvement.

@@ -357,6 +358,8 @@ def secure_filename(filename: str) -> str:
Args:
filename (str): Arbitrary filename input from the request, such as a
multipart form filename field.
max_length (int, optional): Maximum length of the sanitized filename,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (int, optional) looks a bit unconventional. I think you can simply remove this type from here altogether, and let Sphinx pick it up from the type annotation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to pass the max_length as a parameter in the BodyPart.secure_filename function or set it as an instance or class variable and reference it from there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want us to actually use the new option in BodyPart.secure_filename(), i.e.:

    @property
    def secure_filename(self) -> str:
        """The sanitized version of `filename` using only the most common ASCII
        <...>
        """  # noqa: D205
        try:
            return misc.secure_filename(self.filename or '')
        except ValueError as ex:
            raise MultipartParseError(description=str(ex)) from ex

⬇️

    @property
    def secure_filename(self) -> str:
        """The sanitized version of `filename` using only the most common ASCII
        <...>
        """  # noqa: D205
        try:
            return misc.secure_filename(self.filename or '', max_length=self._parse_options.max_secure_filename_length)
        except ValueError as ex:
            raise MultipartParseError(description=str(ex)) from ex

(untested)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants