summaryrefslogtreecommitdiff
path: root/tests/file_uploads
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2021-04-14 18:23:44 +0200
committerCarlton Gibson <carlton.gibson@noumenal.es>2021-05-04 08:44:42 +0200
commit0b79eb36915d178aef5c6a7bbce71b1e76d376d3 (patch)
treeceb3f3df98ca1ee553f793121b6e43dc67ee2607 /tests/file_uploads
parent8de4ca74ba49b3f97a252e2b9d385cb2e70c442c (diff)
downloaddjango-0b79eb36915d178aef5c6a7bbce71b1e76d376d3.tar.gz
Fixed CVE-2021-31542 -- Tightened path & file name sanitation in file uploads.
Diffstat (limited to 'tests/file_uploads')
-rw-r--r--tests/file_uploads/tests.py38
1 files changed, 37 insertions, 1 deletions
diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py
index e8f91e2fa0..7bc8d41dac 100644
--- a/tests/file_uploads/tests.py
+++ b/tests/file_uploads/tests.py
@@ -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 = [
'..&sol;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",