diff options
author | Florian Apolloner <florian@apolloner.eu> | 2021-04-14 18:23:44 +0200 |
---|---|---|
committer | Carlton Gibson <carlton.gibson@noumenal.es> | 2021-05-04 08:44:42 +0200 |
commit | 0b79eb36915d178aef5c6a7bbce71b1e76d376d3 (patch) | |
tree | ceb3f3df98ca1ee553f793121b6e43dc67ee2607 /tests/file_uploads | |
parent | 8de4ca74ba49b3f97a252e2b9d385cb2e70c442c (diff) | |
download | django-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.py | 38 |
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 = [ '../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", |