Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:fmccarthy:branches:Cloud:OpenStack:Pike
python-Django
CVE-2021-45452.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File CVE-2021-45452.patch of Package python-Django
From e63994b1e6ab8b66d9c5fc2f4bf5cd8d09e749e1 Mon Sep 17 00:00:00 2001 From: Florian Apolloner <florian@apolloner.eu> Date: Fri, 17 Dec 2021 21:07:50 +0100 Subject: [PATCH] [2.2.x] Fixed CVE-2021-45452 -- Fixed potential path traversal in storage subsystem. Thanks to Dennis Brinkrolf for the report. --- django/core/files/storage.py | 9 ++++++++- docs/releases/2.2.26.txt | 9 +++++++++ tests/file_storage/test_generate_filename.py | 19 +++++++++++++------ tests/file_storage/tests.py | 6 ++++++ 4 files changed, 36 insertions(+), 7 deletions(-) Index: Django-1.11.29/django/core/files/storage.py =================================================================== --- Django-1.11.29.orig/django/core/files/storage.py +++ Django-1.11.29/django/core/files/storage.py @@ -53,7 +53,10 @@ class Storage(object): content = File(content, name) name = self.get_available_name(name, max_length=max_length) - return self._save(name, content) + name = self._save(name, content) + # Ensure that the name returned from the storage system is still valid. + validate_file_name(name, allow_relative_path=True) + return name # These methods are part of the public API, with default implementations. @@ -69,6 +72,7 @@ class Storage(object): Returns a filename that's free on the target storage system, and available for new content to be written to. """ + name = str(name).replace('\\', '/') 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) @@ -103,6 +107,7 @@ class Storage(object): Validate the filename by calling get_valid_name() and return a filename to be passed to the save() method. """ + filename = str(filename).replace('\\', '/') # `filename` may include a path as returned by FileField.upload_to. dirname, filename = os.path.split(filename) if '..' in pathlib.PurePath(dirname).parts: @@ -380,6 +385,8 @@ class FileSystemStorage(Storage): if self.file_permissions_mode is not None: os.chmod(full_path, self.file_permissions_mode) + # Ensure the saved path is always relative to the storage root. + name = os.path.relpath(full_path, self.location) # Store filenames with forward slashes, even on Windows. return force_text(name.replace('\\', '/')) Index: Django-1.11.29/tests/file_storage/test_generate_filename.py =================================================================== --- Django-1.11.29.orig/tests/file_storage/test_generate_filename.py +++ Django-1.11.29/tests/file_storage/test_generate_filename.py @@ -54,13 +54,20 @@ class GenerateFilenameStorageTests(Simpl s.generate_filename(file_name) def test_storage_dangerous_paths_dir_name(self): - file_name = '/tmp/../path' + candidates = [ + ('tmp/../path', 'tmp/..'), + ('tmp\\..\\path', 'tmp/..'), + ('/tmp/../path', '/tmp/..'), + ('\\tmp\\..\\path', '/tmp/..'), + ] s = FileSystemStorage() - msg = "Detected path traversal attempt in '/tmp/..'" - with self.assertRaisesMessage(SuspiciousFileOperation, msg): - s.get_available_name(file_name) - with self.assertRaisesMessage(SuspiciousFileOperation, msg): - s.generate_filename(file_name) + for file_name, path in candidates: + msg = "Detected path traversal attempt in '%s'" % path + with self.subTest(file_name=file_name): + with self.assertRaisesMessage(SuspiciousFileOperation, msg): + s.get_available_name(file_name) + with self.assertRaisesMessage(SuspiciousFileOperation, msg): + s.generate_filename(file_name) def test_filefield_dangerous_filename(self): candidates = ['..', '.', '', '???', '$.$.$'] Index: Django-1.11.29/tests/file_storage/tests.py =================================================================== --- Django-1.11.29.orig/tests/file_storage/tests.py +++ Django-1.11.29/tests/file_storage/tests.py @@ -342,6 +342,12 @@ class FileStorageTests(SimpleTestCase): self.storage.delete('path/to/test.file') + def test_file_save_abs_path(self): + test_name = 'path/to/test.file' + f = ContentFile('file saved with path') + f_name = self.storage.save(os.path.join(self.temp_dir, test_name), f) + self.assertEqual(f_name, test_name) + def test_save_doesnt_close(self): with TemporaryUploadedFile('test', 'text/plain', 1, 'utf8') as file: file.write(b'1') Index: Django-1.11.29/django/core/files/utils.py =================================================================== --- Django-1.11.29.orig/django/core/files/utils.py +++ Django-1.11.29/django/core/files/utils.py @@ -1,18 +1,26 @@ import os +import pathlib from django.core.exceptions import SuspiciousFileOperation - -def validate_file_name(name): - if name != os.path.basename(name): - raise SuspiciousFileOperation("File name '%s' includes path elements" % name) - +def validate_file_name(name, allow_relative_path=False): # Remove potentially dangerous names - if name in {'', '.', '..'}: + if os.path.basename(name) in {'', '.', '..'}: raise SuspiciousFileOperation("Could not derive file name from '%s'" % name) - return name + if allow_relative_path: + # Use PurePosixPath() because this branch is checked only in + # FileField.generate_filename() where all file paths are expected to be + # Unix style (with forward slashes). + path = pathlib.PurePosixPath(name) + if path.is_absolute() or '..' in path.parts: + raise SuspiciousFileOperation( + "Detected path traversal attempt in '%s'" % name + ) + elif name != os.path.basename(name): + raise SuspiciousFileOperation("File name '%s' includes path elements" % name) + return name class FileProxyMixin(object): """
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor