66from django .core .exceptions import ImproperlyConfigured
77from django .core .exceptions import SuspiciousFileOperation
88from django .core .files .utils import FileProxyMixin
9+ from django .core .files .utils import validate_file_name
910from django .utils .encoding import force_bytes
1011
1112
@@ -121,11 +122,18 @@ def lookup_env(names):
121122
122123
123124def get_available_overwrite_name (name , max_length ):
125+ # This is adapted from Django, and will be removed once
126+ # Django 5.1 is the lowest supported version
127+ dir_name , file_name = os .path .split (name )
128+ if ".." in pathlib .PurePath (dir_name ).parts :
129+ raise SuspiciousFileOperation (
130+ "Detected path traversal attempt in '%s'" % dir_name
131+ )
132+ validate_file_name (file_name , allow_relative_path = True )
133+
124134 if max_length is None or len (name ) <= max_length :
125135 return name
126136
127- # Adapted from Django
128- dir_name , file_name = os .path .split (name )
129137 file_root , file_ext = os .path .splitext (file_name )
130138 truncation = len (name ) - max_length
131139
@@ -136,7 +144,9 @@ def get_available_overwrite_name(name, max_length):
136144 "Please make sure that the corresponding file field "
137145 'allows sufficient "max_length".' % name
138146 )
139- return os .path .join (dir_name , "{}{}" .format (file_root , file_ext ))
147+ name = os .path .join (dir_name , "{}{}" .format (file_root , file_ext ))
148+ validate_file_name (name , allow_relative_path = True )
149+ return name
140150
141151
142152def is_seekable (file_object ):
0 commit comments