diff options
| author | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-04-13 10:10:56 +0200 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-05-03 13:43:16 +0200 |
| commit | 21b1b1fc03e5f9e9f8c977ee6e35618dd3b353dd (patch) | |
| tree | 0fc243d8c7e71dae06ace8c746fc88d24d52fed3 /tests/forms_tests | |
| parent | 290fd5ecece400490ad6bb557720d3b76f647eaf (diff) | |
[4.2.x] Fixed CVE-2023-31047, Fixed #31710 -- Prevented potential bypass of validation when uploading multiple files using one form field.
Thanks Moataz Al-Sharida and nawaik for reports.
Co-authored-by: Shai Berger <shai@platonix.com>
Co-authored-by: nessita <124304+nessita@users.noreply.github.com>
Diffstat (limited to 'tests/forms_tests')
| -rw-r--r-- | tests/forms_tests/field_tests/test_filefield.py | 68 | ||||
| -rw-r--r-- | tests/forms_tests/widget_tests/test_clearablefileinput.py | 5 | ||||
| -rw-r--r-- | tests/forms_tests/widget_tests/test_fileinput.py | 44 |
3 files changed, 116 insertions, 1 deletions
diff --git a/tests/forms_tests/field_tests/test_filefield.py b/tests/forms_tests/field_tests/test_filefield.py index 56aaa311e0..00c74a7c1a 100644 --- a/tests/forms_tests/field_tests/test_filefield.py +++ b/tests/forms_tests/field_tests/test_filefield.py @@ -2,7 +2,8 @@ import pickle from django.core.exceptions import ValidationError from django.core.files.uploadedfile import SimpleUploadedFile -from django.forms import FileField +from django.core.validators import validate_image_file_extension +from django.forms import FileField, FileInput from django.test import SimpleTestCase @@ -109,3 +110,68 @@ class FileFieldTest(SimpleTestCase): def test_file_picklable(self): self.assertIsInstance(pickle.loads(pickle.dumps(FileField())), FileField) + + +class MultipleFileInput(FileInput): + allow_multiple_selected = True + + +class MultipleFileField(FileField): + def __init__(self, *args, **kwargs): + kwargs.setdefault("widget", MultipleFileInput()) + super().__init__(*args, **kwargs) + + def clean(self, data, initial=None): + single_file_clean = super().clean + if isinstance(data, (list, tuple)): + result = [single_file_clean(d, initial) for d in data] + else: + result = single_file_clean(data, initial) + return result + + +class MultipleFileFieldTest(SimpleTestCase): + def test_file_multiple(self): + f = MultipleFileField() + files = [ + SimpleUploadedFile("name1", b"Content 1"), + SimpleUploadedFile("name2", b"Content 2"), + ] + self.assertEqual(f.clean(files), files) + + def test_file_multiple_empty(self): + f = MultipleFileField() + files = [ + SimpleUploadedFile("empty", b""), + SimpleUploadedFile("nonempty", b"Some Content"), + ] + msg = "'The submitted file is empty.'" + with self.assertRaisesMessage(ValidationError, msg): + f.clean(files) + with self.assertRaisesMessage(ValidationError, msg): + f.clean(files[::-1]) + + def test_file_multiple_validation(self): + f = MultipleFileField(validators=[validate_image_file_extension]) + + good_files = [ + SimpleUploadedFile("image1.jpg", b"fake JPEG"), + SimpleUploadedFile("image2.png", b"faux image"), + SimpleUploadedFile("image3.bmp", b"fraudulent bitmap"), + ] + self.assertEqual(f.clean(good_files), good_files) + + evil_files = [ + SimpleUploadedFile("image1.sh", b"#!/bin/bash -c 'echo pwned!'\n"), + SimpleUploadedFile("image2.png", b"faux image"), + SimpleUploadedFile("image3.jpg", b"fake JPEG"), + ] + + evil_rotations = ( + evil_files[i:] + evil_files[:i] # Rotate by i. + for i in range(len(evil_files)) + ) + msg = "File extension “sh” is not allowed. Allowed extensions are: " + for rotated_evil_files in evil_rotations: + with self.assertRaisesMessage(ValidationError, msg): + f.clean(rotated_evil_files) diff --git a/tests/forms_tests/widget_tests/test_clearablefileinput.py b/tests/forms_tests/widget_tests/test_clearablefileinput.py index 4fbaec0910..a9b0667ca9 100644 --- a/tests/forms_tests/widget_tests/test_clearablefileinput.py +++ b/tests/forms_tests/widget_tests/test_clearablefileinput.py @@ -242,3 +242,8 @@ class ClearableFileInputTest(WidgetTest): '<input type="file" name="clearable_file" id="id_clearable_file"></div>', form.render(), ) + + def test_multiple_error(self): + msg = "ClearableFileInput doesn't support uploading multiple files." + with self.assertRaisesMessage(ValueError, msg): + ClearableFileInput(attrs={"multiple": True}) diff --git a/tests/forms_tests/widget_tests/test_fileinput.py b/tests/forms_tests/widget_tests/test_fileinput.py index ea73e577ce..a49f481728 100644 --- a/tests/forms_tests/widget_tests/test_fileinput.py +++ b/tests/forms_tests/widget_tests/test_fileinput.py @@ -1,4 +1,6 @@ +from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import FileField, FileInput, Form +from django.utils.datastructures import MultiValueDict from .base import WidgetTest @@ -48,3 +50,45 @@ class FileInputTest(WidgetTest): 'name="field" required type="file"></div>', form.render(), ) + + def test_multiple_error(self): + msg = "FileInput doesn't support uploading multiple files." + with self.assertRaisesMessage(ValueError, msg): + FileInput(attrs={"multiple": True}) + + def test_value_from_datadict_multiple(self): + class MultipleFileInput(FileInput): + allow_multiple_selected = True + + file_1 = SimpleUploadedFile("something1.txt", b"content 1") + file_2 = SimpleUploadedFile("something2.txt", b"content 2") + # Uploading multiple files is allowed. + widget = MultipleFileInput(attrs={"multiple": True}) + value = widget.value_from_datadict( + data={"name": "Test name"}, + files=MultiValueDict({"myfile": [file_1, file_2]}), + name="myfile", + ) + self.assertEqual(value, [file_1, file_2]) + # Uploading multiple files is not allowed. + widget = FileInput() + value = widget.value_from_datadict( + data={"name": "Test name"}, + files=MultiValueDict({"myfile": [file_1, file_2]}), + name="myfile", + ) + self.assertEqual(value, file_2) + + def test_multiple_default(self): + class MultipleFileInput(FileInput): + allow_multiple_selected = True + + tests = [ + (None, True), + ({"class": "myclass"}, True), + ({"multiple": False}, False), + ] + for attrs, expected in tests: + with self.subTest(attrs=attrs): + widget = MultipleFileInput(attrs=attrs) + self.assertIs(widget.attrs["multiple"], expected) |
