Skip to content

Commit

Permalink
Apply additional validation in overwrite path (#1486)
Browse files Browse the repository at this point in the history
  • Loading branch information
jschneier authored Feb 15, 2025
1 parent 394e5d6 commit 5db357a
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 3 deletions.
16 changes: 13 additions & 3 deletions storages/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import SuspiciousFileOperation
from django.core.files.utils import FileProxyMixin
from django.core.files.utils import validate_file_name
from django.utils.encoding import force_bytes


Expand Down Expand Up @@ -121,11 +122,18 @@ def lookup_env(names):


def get_available_overwrite_name(name, max_length):
# This is adapted from Django, and will be removed once
# Django 5.1 is the lowest supported version
dir_name, file_name = os.path.split(name)
if ".." in pathlib.PurePath(dir_name).parts:
raise SuspiciousFileOperation(
"Detected path traversal attempt in '%s'" % dir_name
)
validate_file_name(file_name, allow_relative_path=True)

if max_length is None or len(name) <= max_length:
return name

# Adapted from Django
dir_name, file_name = os.path.split(name)
file_root, file_ext = os.path.splitext(file_name)
truncation = len(name) - max_length

Expand All @@ -136,7 +144,9 @@ def get_available_overwrite_name(name, max_length):
"Please make sure that the corresponding file field "
'allows sufficient "max_length".' % name
)
return os.path.join(dir_name, "{}{}".format(file_root, file_ext))
name = os.path.join(dir_name, "{}{}".format(file_root, file_ext))
validate_file_name(name, allow_relative_path=True)
return name


def is_seekable(file_object):
Expand Down
5 changes: 5 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ def test_truncates_away_filename_raises(self):
with self.assertRaises(SuspiciousFileOperation):
gaon(name, len(name) - 5)

def test_suspicious_file(self):
name = "superlong/file/with/../path.txt"
with self.assertRaises(SuspiciousFileOperation):
gaon(name, 50)


class TestReadBytesWrapper(TestCase):
def test_with_bytes_file(self):
Expand Down

0 comments on commit 5db357a

Please sign in to comment.