summaryrefslogtreecommitdiff
path: root/tests/forms_tests
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2023-04-13 10:10:56 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-05-03 13:42:00 +0200
commitfb4c55d9ec4bb812a7fb91fa20510d91645e411b (patch)
treed903883e061b3412efa07a424d26964532113f4a /tests/forms_tests
parent8e2460d599aec95f8cfe514d3cc8acdd4ca4b1fb (diff)
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.py68
-rw-r--r--tests/forms_tests/widget_tests/test_clearablefileinput.py5
-rw-r--r--tests/forms_tests/widget_tests/test_fileinput.py44
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 8d5f0c5f98..53b84cc0ec 100644
--- a/tests/forms_tests/widget_tests/test_clearablefileinput.py
+++ b/tests/forms_tests/widget_tests/test_clearablefileinput.py
@@ -245,3 +245,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)