mirror of
https://github.com/django/django.git
synced 2025-10-14 20:41:00 +00:00
Fixed CVE-2021-31542 -- Tightened path & file name sanitation in file uploads.
This commit is contained in:
parent
8de4ca74ba
commit
0b79eb3691
14 changed files with 190 additions and 13 deletions
|
@ -9,8 +9,9 @@ from io import BytesIO, StringIO
|
|||
from unittest import mock
|
||||
from urllib.parse import quote
|
||||
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from django.core.files import temp as tempfile
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
|
||||
from django.http.multipartparser import (
|
||||
FILE, MultiPartParser, MultiPartParserError, Parser, parse_header,
|
||||
)
|
||||
|
@ -39,6 +40,16 @@ CANDIDATE_TRAVERSAL_FILE_NAMES = [
|
|||
'../hax0rd.txt', # HTML entities.
|
||||
]
|
||||
|
||||
CANDIDATE_INVALID_FILE_NAMES = [
|
||||
'/tmp/', # Directory, *nix-style.
|
||||
'c:\\tmp\\', # Directory, win-style.
|
||||
'/tmp/.', # Directory dot, *nix-style.
|
||||
'c:\\tmp\\.', # Directory dot, *nix-style.
|
||||
'/tmp/..', # Parent directory, *nix-style.
|
||||
'c:\\tmp\\..', # Parent directory, win-style.
|
||||
'', # Empty filename.
|
||||
]
|
||||
|
||||
|
||||
@override_settings(MEDIA_ROOT=MEDIA_ROOT, ROOT_URLCONF='file_uploads.urls', MIDDLEWARE=[])
|
||||
class FileUploadTests(TestCase):
|
||||
|
@ -53,6 +64,22 @@ class FileUploadTests(TestCase):
|
|||
shutil.rmtree(MEDIA_ROOT)
|
||||
super().tearDownClass()
|
||||
|
||||
def test_upload_name_is_validated(self):
|
||||
candidates = [
|
||||
'/tmp/',
|
||||
'/tmp/..',
|
||||
'/tmp/.',
|
||||
]
|
||||
if sys.platform == 'win32':
|
||||
candidates.extend([
|
||||
'c:\\tmp\\',
|
||||
'c:\\tmp\\..',
|
||||
'c:\\tmp\\.',
|
||||
])
|
||||
for file_name in candidates:
|
||||
with self.subTest(file_name=file_name):
|
||||
self.assertRaises(SuspiciousFileOperation, UploadedFile, name=file_name)
|
||||
|
||||
def test_simple_upload(self):
|
||||
with open(__file__, 'rb') as fp:
|
||||
post_data = {
|
||||
|
@ -718,6 +745,15 @@ class MultiParserTests(SimpleTestCase):
|
|||
with self.subTest(file_name=file_name):
|
||||
self.assertEqual(parser.sanitize_file_name(file_name), 'hax0rd.txt')
|
||||
|
||||
def test_sanitize_invalid_file_name(self):
|
||||
parser = MultiPartParser({
|
||||
'CONTENT_TYPE': 'multipart/form-data; boundary=_foo',
|
||||
'CONTENT_LENGTH': '1',
|
||||
}, StringIO('x'), [], 'utf-8')
|
||||
for file_name in CANDIDATE_INVALID_FILE_NAMES:
|
||||
with self.subTest(file_name=file_name):
|
||||
self.assertIsNone(parser.sanitize_file_name(file_name))
|
||||
|
||||
def test_rfc2231_parsing(self):
|
||||
test_data = (
|
||||
(b"Content-Type: application/x-stuff; title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue