@@ -51,7 +51,10 @@ def save(self, name, content, max_length=None):
5151 content = File (content , name )
5252
5353 name = self .get_available_name (name , max_length = max_length )
54- return self ._save (name , content )
54+ name = self ._save (name , content )
55+ # Ensure that the name returned from the storage system is still valid.
56+ validate_file_name (name , allow_relative_path = True )
57+ return name
5558
5659 # These methods are part of the public API, with default implementations.
5760
@@ -75,6 +78,7 @@ def get_available_name(self, name, max_length=None):
7578 Return a filename that's free on the target storage system and
7679 available for new content to be written to.
7780 """
81+ name = str (name ).replace ('\\ ' , '/' )
7882 dir_name , file_name = os .path .split (name )
7983 if '..' in pathlib .PurePath (dir_name ).parts :
8084 raise SuspiciousFileOperation ("Detected path traversal attempt in '%s'" % dir_name )
@@ -108,6 +112,7 @@ def generate_filename(self, filename):
108112 Validate the filename by calling get_valid_name() and return a filename
109113 to be passed to the save() method.
110114 """
115+ filename = str (filename ).replace ('\\ ' , '/' )
111116 # `filename` may include a path as returned by FileField.upload_to.
112117 dirname , filename = os .path .split (filename )
113118 if '..' in pathlib .PurePath (dirname ).parts :
@@ -297,6 +302,8 @@ def _save(self, name, content):
297302 if self .file_permissions_mode is not None :
298303 os .chmod (full_path , self .file_permissions_mode )
299304
305+ # Ensure the saved path is always relative to the storage root.
306+ name = os .path .relpath (full_path , self .location )
300307 # Store filenames with forward slashes, even on Windows.
301308 return str (name ).replace ('\\ ' , '/' )
302309
0 commit comments