diff -Nru python-django-1.11.11/debian/changelog python-django-1.11.11/debian/changelog --- python-django-1.11.11/debian/changelog 2023-02-08 15:30:23.000000000 +0000 +++ python-django-1.11.11/debian/changelog 2023-04-26 14:05:28.000000000 +0000 @@ -1,3 +1,16 @@ +python-django (1:1.11.11-1ubuntu1.21) bionic-security; urgency=medium + + * SECURITY UPDATE: Potential bypass of validation when uploading multiple + files using one form field + - debian/patches/CVE-2023-31047.patch: prevent uploading multiple files + in django/forms/widgets.py, docs/topics/http/file-uploads.txt, + tests/forms_tests/field_tests/test_filefield.py, + tests/forms_tests/widget_tests/test_clearablefileinput.py, + tests/forms_tests/widget_tests/test_fileinput.py. + - CVE-2023-31047 + + -- Marc Deslauriers Wed, 26 Apr 2023 10:05:28 -0400 + python-django (1:1.11.11-1ubuntu1.20) bionic-security; urgency=medium * SECURITY UPDATE: Potential denial-of-service in file uploads diff -Nru python-django-1.11.11/debian/patches/CVE-2023-31047.patch python-django-1.11.11/debian/patches/CVE-2023-31047.patch --- python-django-1.11.11/debian/patches/CVE-2023-31047.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-django-1.11.11/debian/patches/CVE-2023-31047.patch 2023-04-26 14:05:28.000000000 +0000 @@ -0,0 +1,332 @@ +Backport of: + +From ed1eb009b587bf90b7ddff7ed25bff67b7dc9625 Mon Sep 17 00:00:00 2001 +From: Mariusz Felisiak +Date: Thu, 13 Apr 2023 10:10:56 +0200 +Subject: [PATCH] [3.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 +Co-authored-by: nessita <124304+nessita@users.noreply.github.com> +--- + django/forms/widgets.py | 26 ++++++- + docs/releases/3.2.19.txt | 16 ++++- + docs/topics/http/file-uploads.txt | 65 ++++++++++++++++-- + .../forms_tests/field_tests/test_filefield.py | 68 ++++++++++++++++++- + .../widget_tests/test_clearablefileinput.py | 5 ++ + .../widget_tests/test_fileinput.py | 44 ++++++++++++ + 6 files changed, 215 insertions(+), 9 deletions(-) + +#diff --git a/docs/releases/3.2.19.txt b/docs/releases/3.2.19.txt +#index c5817e689c..9f9eb3f45c 100644 +#--- a/docs/releases/3.2.19.txt +#+++ b/docs/releases/3.2.19.txt +#@@ -6,4 +6,18 @@ Django 3.2.19 release notes +# +# Django 3.2.19 fixes a security issue with severity "low" in 3.2.18. +# +#-... +#+CVE-2023-31047: Potential bypass of validation when uploading multiple files using one form field +#+================================================================================================= +#+ +#+Uploading multiple files using one form field has never been supported by +#+:class:`.forms.FileField` or :class:`.forms.ImageField` as only the last +#+uploaded file was validated. Unfortunately, :ref:`uploading_multiple_files` +#+topic suggested otherwise. +#+ +#+In order to avoid the vulnerability, :class:`~django.forms.ClearableFileInput` +#+and :class:`~django.forms.FileInput` form widgets now raise ``ValueError`` when +#+the ``multiple`` HTML attribute is set on them. To prevent the exception and +#+keep the old behavior, set ``allow_multiple_selected`` to ``True``. +#+ +#+For more details on using the new attribute and handling of multiple files +#+through a single field, see :ref:`uploading_multiple_files`. +--- a/django/forms/widgets.py ++++ b/django/forms/widgets.py +@@ -355,16 +355,40 @@ class MultipleHiddenInput(HiddenInput): + + class FileInput(Input): + input_type = 'file' ++ allow_multiple_selected = False + needs_multipart_form = True + template_name = 'django/forms/widgets/file.html' + ++ def __init__(self, attrs=None): ++ if ( ++ attrs is not None and ++ not self.allow_multiple_selected and ++ attrs.get("multiple", False) ++ ): ++ raise ValueError( ++ "%s doesn't support uploading multiple files." ++ % self.__class__.__name__ ++ ) ++ if self.allow_multiple_selected: ++ if attrs is None: ++ attrs = {"multiple": True} ++ else: ++ attrs.setdefault("multiple", True) ++ super(FileInput, self).__init__(attrs) ++ + def format_value(self, value): + """File input never renders a value.""" + return + + def value_from_datadict(self, data, files, name): + "File widgets take data from FILES, not POST" +- return files.get(name) ++ getter = files.get ++ if self.allow_multiple_selected: ++ try: ++ getter = files.getlist ++ except AttributeError: ++ pass ++ return getter(name) + + def value_omitted_from_data(self, data, files, name): + return name not in files +--- a/docs/topics/http/file-uploads.txt ++++ b/docs/topics/http/file-uploads.txt +@@ -127,19 +127,54 @@ field in the model:: + form = UploadFileForm() + return render(request, 'upload.html', {'form': form}) + ++.. _uploading_multiple_files: ++ + Uploading multiple files + ------------------------ + +-If you want to upload multiple files using one form field, set the ``multiple`` +-HTML attribute of field's widget: ++.. ++ Tests in tests.forms_tests.field_tests.test_filefield.MultipleFileFieldTest ++ should be updated after any changes in the following snippets. ++ ++If you want to upload multiple files using one form field, create a subclass ++of the field's widget and set the ``allow_multiple_selected`` attribute on it ++to ``True``. ++ ++In order for such files to be all validated by your form (and have the value of ++the field include them all), you will also have to subclass ``FileField``. See ++below for an example. ++ ++.. admonition:: Multiple file field ++ ++ Django is likely to have a proper multiple file field support at some point ++ in the future. + + .. snippet:: + :filename: forms.py + + from django import forms + ++ ++ class MultipleFileInput(forms.ClearableFileInput): ++ allow_multiple_selected = True ++ ++ ++ class MultipleFileField(forms.FileField): ++ def __init__(self, *args, **kwargs): ++ kwargs.setdefault("widget", MultipleFileInput()) ++ super(MultipleFileField, self).__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 FileFieldForm(forms.Form): +- file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True})) ++ file_field = MultipleFileField() + + Then override the ``post`` method of your + :class:`~django.views.generic.edit.FormView` subclass to handle multiple file +@@ -159,14 +194,32 @@ uploads: + def post(self, request, *args, **kwargs): + form_class = self.get_form_class() + form = self.get_form(form_class) +- files = request.FILES.getlist('file_field') + if form.is_valid(): +- for f in files: +- ... # Do something with each file. + return self.form_valid(form) + else: + return self.form_invalid(form) + ++ def form_valid(self, form): ++ files = form.cleaned_data["file_field"] ++ for f in files: ++ ... # Do something with each file. ++ return super().form_valid() ++ ++.. warning:: ++ ++ This will allow you to handle multiple files at the form level only. Be ++ aware that you cannot use it to put multiple files on a single model ++ instance (in a single field), for example, even if the custom widget is used ++ with a form field related to a model ``FileField``. ++ ++.. versionchanged:: 3.2.19 ++ ++ In previous versions, there was no support for the ``allow_multiple_selected`` ++ class attribute, and users were advised to create the widget with the HTML ++ attribute ``multiple`` set through the ``attrs`` argument. However, this ++ caused validation of the form field to be applied only to the last file ++ submitted, which could have adverse security implications. ++ + Upload Handlers + =============== + +--- a/tests/forms_tests/field_tests/test_filefield.py ++++ b/tests/forms_tests/field_tests/test_filefield.py +@@ -4,7 +4,8 @@ from __future__ import unicode_literals + import pickle + + from django.core.files.uploadedfile import SimpleUploadedFile +-from django.forms import FileField, ValidationError ++from django.core.validators import validate_image_file_extension ++from django.forms import FileField, FileInput, ValidationError + from django.test import SimpleTestCase + + +@@ -85,3 +86,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(MultipleFileField, self).__init__(*args, **kwargs) ++ ++ def clean(self, data, initial=None): ++ single_file_clean = super(MultipleFileField, self).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) +--- a/tests/forms_tests/widget_tests/test_clearablefileinput.py ++++ b/tests/forms_tests/widget_tests/test_clearablefileinput.py +@@ -167,3 +167,8 @@ class ClearableFileInputTest(WidgetTest) + self.assertIs(widget.value_omitted_from_data({}, {}, 'field'), True) + self.assertIs(widget.value_omitted_from_data({}, {'field': 'x'}, 'field'), False) + self.assertIs(widget.value_omitted_from_data({'field-clear': 'y'}, {}, 'field'), False) ++ ++ def test_multiple_error(self): ++ msg = "ClearableFileInput doesn't support uploading multiple files." ++ with self.assertRaisesMessage(ValueError, msg): ++ ClearableFileInput(attrs={"multiple": True}) +--- 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 FileInput ++from django.utils.datastructures import MultiValueDict + + from .base import WidgetTest + +@@ -18,3 +20,44 @@ class FileInputTest(WidgetTest): + def test_value_omitted_from_data(self): + self.assertIs(self.widget.value_omitted_from_data({}, {}, 'field'), True) + self.assertIs(self.widget.value_omitted_from_data({}, {'field': 'value'}, 'field'), False) ++ ++ 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: ++ widget = MultipleFileInput(attrs=attrs) ++ self.assertIs(widget.attrs["multiple"], expected) diff -Nru python-django-1.11.11/debian/patches/series python-django-1.11.11/debian/patches/series --- python-django-1.11.11/debian/patches/series 2023-02-08 15:29:15.000000000 +0000 +++ python-django-1.11.11/debian/patches/series 2023-04-26 14:03:37.000000000 +0000 @@ -35,3 +35,4 @@ CVE-2022-34265.patch CVE-2023-23969.patch CVE-2023-24580.patch +CVE-2023-31047.patch