summaryrefslogtreecommitdiffstats
path: root/meta-python
diff options
context:
space:
mode:
Diffstat (limited to 'meta-python')
-rw-r--r--meta-python/recipes-devtools/python/python3-django/CVE-2023-31047.patch352
-rw-r--r--meta-python/recipes-devtools/python/python3-django_2.2.28.bb2
2 files changed, 354 insertions, 0 deletions
diff --git a/meta-python/recipes-devtools/python/python3-django/CVE-2023-31047.patch b/meta-python/recipes-devtools/python/python3-django/CVE-2023-31047.patch
new file mode 100644
index 0000000000..ab29a2ed97
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-django/CVE-2023-31047.patch
@@ -0,0 +1,352 @@
1From fd3215dec5d50aa1f09cb1f8eba193524e7379f3 Mon Sep 17 00:00:00 2001
2From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
3Date: Thu, 25 May 2023 14:49:15 +0000
4Subject: [PATCH] Fixed CVE-2023-31047, Fixed #31710
5
6-- Prevented potential bypass of validation when uploading multiple files using one form field.
7
8Thanks Moataz Al-Sharida and nawaik for reports.
9
10Co-authored-by: Shai Berger <shai@platonix.com>
11Co-authored-by: nessita <124304+nessita@users.noreply.github.com>
12
13CVE: CVE-2023-31047
14
15Upstream-Status: Backport [https://github.com/django/django/commit/fb4c55d9ec4bb812a7fb91fa20510d91645e411b]
16
17Signed-off-by: Narpat Mali <narpat.mali@windriver.com>
18---
19 django/forms/widgets.py | 26 ++++++-
20 docs/releases/2.2.28.txt | 18 +++++
21 docs/topics/http/file-uploads.txt | 65 ++++++++++++++++--
22 .../forms_tests/field_tests/test_filefield.py | 68 ++++++++++++++++++-
23 .../widget_tests/test_clearablefileinput.py | 5 ++
24 .../widget_tests/test_fileinput.py | 44 ++++++++++++
25 6 files changed, 218 insertions(+), 8 deletions(-)
26
27diff --git a/django/forms/widgets.py b/django/forms/widgets.py
28index e37036c..d0cc131 100644
29--- a/django/forms/widgets.py
30+++ b/django/forms/widgets.py
31@@ -372,17 +372,41 @@ class MultipleHiddenInput(HiddenInput):
32
33
34 class FileInput(Input):
35+ allow_multiple_selected = False
36 input_type = 'file'
37 needs_multipart_form = True
38 template_name = 'django/forms/widgets/file.html'
39
40+ def __init__(self, attrs=None):
41+ if (
42+ attrs is not None
43+ and not self.allow_multiple_selected
44+ and attrs.get("multiple", False)
45+ ):
46+ raise ValueError(
47+ "%s doesn't support uploading multiple files."
48+ % self.__class__.__qualname__
49+ )
50+ if self.allow_multiple_selected:
51+ if attrs is None:
52+ attrs = {"multiple": True}
53+ else:
54+ attrs.setdefault("multiple", True)
55+ super().__init__(attrs)
56+
57 def format_value(self, value):
58 """File input never renders a value."""
59 return
60
61 def value_from_datadict(self, data, files, name):
62 "File widgets take data from FILES, not POST"
63- return files.get(name)
64+ getter = files.get
65+ if self.allow_multiple_selected:
66+ try:
67+ getter = files.getlist
68+ except AttributeError:
69+ pass
70+ return getter(name)
71
72 def value_omitted_from_data(self, data, files, name):
73 return name not in files
74diff --git a/docs/releases/2.2.28.txt b/docs/releases/2.2.28.txt
75index 43270fc..854c6b0 100644
76--- a/docs/releases/2.2.28.txt
77+++ b/docs/releases/2.2.28.txt
78@@ -20,3 +20,21 @@ CVE-2022-28347: Potential SQL injection via ``QuerySet.explain(**options)`` on P
79 :meth:`.QuerySet.explain` method was subject to SQL injection in option names,
80 using a suitably crafted dictionary, with dictionary expansion, as the
81 ``**options`` argument.
82+
83+Backporting the CVE-2023-31047 fix on Django 2.2.28.
84+
85+CVE-2023-31047: Potential bypass of validation when uploading multiple files using one form field
86+=================================================================================================
87+
88+Uploading multiple files using one form field has never been supported by
89+:class:`.forms.FileField` or :class:`.forms.ImageField` as only the last
90+uploaded file was validated. Unfortunately, :ref:`uploading_multiple_files`
91+topic suggested otherwise.
92+
93+In order to avoid the vulnerability, :class:`~django.forms.ClearableFileInput`
94+and :class:`~django.forms.FileInput` form widgets now raise ``ValueError`` when
95+the ``multiple`` HTML attribute is set on them. To prevent the exception and
96+keep the old behavior, set ``allow_multiple_selected`` to ``True``.
97+
98+For more details on using the new attribute and handling of multiple files
99+through a single field, see :ref:`uploading_multiple_files`.
100diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt
101index 21a6f06..c1ffb80 100644
102--- a/docs/topics/http/file-uploads.txt
103+++ b/docs/topics/http/file-uploads.txt
104@@ -127,19 +127,54 @@ field in the model::
105 form = UploadFileForm()
106 return render(request, 'upload.html', {'form': form})
107
108+.. _uploading_multiple_files:
109+
110 Uploading multiple files
111 ------------------------
112
113-If you want to upload multiple files using one form field, set the ``multiple``
114-HTML attribute of field's widget:
115+..
116+ Tests in tests.forms_tests.field_tests.test_filefield.MultipleFileFieldTest
117+ should be updated after any changes in the following snippets.
118+
119+If you want to upload multiple files using one form field, create a subclass
120+of the field's widget and set the ``allow_multiple_selected`` attribute on it
121+to ``True``.
122+
123+In order for such files to be all validated by your form (and have the value of
124+the field include them all), you will also have to subclass ``FileField``. See
125+below for an example.
126+
127+.. admonition:: Multiple file field
128+
129+ Django is likely to have a proper multiple file field support at some point
130+ in the future.
131
132 .. code-block:: python
133 :caption: forms.py
134
135 from django import forms
136
137+
138+ class MultipleFileInput(forms.ClearableFileInput):
139+ allow_multiple_selected = True
140+
141+
142+ class MultipleFileField(forms.FileField):
143+ def __init__(self, *args, **kwargs):
144+ kwargs.setdefault("widget", MultipleFileInput())
145+ super().__init__(*args, **kwargs)
146+
147+ def clean(self, data, initial=None):
148+ single_file_clean = super().clean
149+ if isinstance(data, (list, tuple)):
150+ result = [single_file_clean(d, initial) for d in data]
151+ else:
152+ result = single_file_clean(data, initial)
153+ return result
154+
155+
156 class FileFieldForm(forms.Form):
157- file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
158+ file_field = MultipleFileField()
159
160 Then override the ``post`` method of your
161 :class:`~django.views.generic.edit.FormView` subclass to handle multiple file
162@@ -159,14 +194,32 @@ uploads:
163 def post(self, request, *args, **kwargs):
164 form_class = self.get_form_class()
165 form = self.get_form(form_class)
166- files = request.FILES.getlist('file_field')
167 if form.is_valid():
168- for f in files:
169- ... # Do something with each file.
170 return self.form_valid(form)
171 else:
172 return self.form_invalid(form)
173
174+ def form_valid(self, form):
175+ files = form.cleaned_data["file_field"]
176+ for f in files:
177+ ... # Do something with each file.
178+ return super().form_valid()
179+
180+.. warning::
181+
182+ This will allow you to handle multiple files at the form level only. Be
183+ aware that you cannot use it to put multiple files on a single model
184+ instance (in a single field), for example, even if the custom widget is used
185+ with a form field related to a model ``FileField``.
186+
187+.. backportedfix:: 2.2.28
188+
189+ In previous versions, there was no support for the ``allow_multiple_selected``
190+ class attribute, and users were advised to create the widget with the HTML
191+ attribute ``multiple`` set through the ``attrs`` argument. However, this
192+ caused validation of the form field to be applied only to the last file
193+ submitted, which could have adverse security implications.
194+
195 Upload Handlers
196 ===============
197
198diff --git a/tests/forms_tests/field_tests/test_filefield.py b/tests/forms_tests/field_tests/test_filefield.py
199index 3357444..ba559ee 100644
200--- a/tests/forms_tests/field_tests/test_filefield.py
201+++ b/tests/forms_tests/field_tests/test_filefield.py
202@@ -1,7 +1,8 @@
203 import pickle
204
205 from django.core.files.uploadedfile import SimpleUploadedFile
206-from django.forms import FileField, ValidationError
207+from django.core.validators import validate_image_file_extension
208+from django.forms import FileField, FileInput, ValidationError
209 from django.test import SimpleTestCase
210
211
212@@ -82,3 +83,68 @@ class FileFieldTest(SimpleTestCase):
213
214 def test_file_picklable(self):
215 self.assertIsInstance(pickle.loads(pickle.dumps(FileField())), FileField)
216+
217+
218+class MultipleFileInput(FileInput):
219+ allow_multiple_selected = True
220+
221+
222+class MultipleFileField(FileField):
223+ def __init__(self, *args, **kwargs):
224+ kwargs.setdefault("widget", MultipleFileInput())
225+ super().__init__(*args, **kwargs)
226+
227+ def clean(self, data, initial=None):
228+ single_file_clean = super().clean
229+ if isinstance(data, (list, tuple)):
230+ result = [single_file_clean(d, initial) for d in data]
231+ else:
232+ result = single_file_clean(data, initial)
233+ return result
234+
235+
236+class MultipleFileFieldTest(SimpleTestCase):
237+ def test_file_multiple(self):
238+ f = MultipleFileField()
239+ files = [
240+ SimpleUploadedFile("name1", b"Content 1"),
241+ SimpleUploadedFile("name2", b"Content 2"),
242+ ]
243+ self.assertEqual(f.clean(files), files)
244+
245+ def test_file_multiple_empty(self):
246+ f = MultipleFileField()
247+ files = [
248+ SimpleUploadedFile("empty", b""),
249+ SimpleUploadedFile("nonempty", b"Some Content"),
250+ ]
251+ msg = "'The submitted file is empty.'"
252+ with self.assertRaisesMessage(ValidationError, msg):
253+ f.clean(files)
254+ with self.assertRaisesMessage(ValidationError, msg):
255+ f.clean(files[::-1])
256+
257+ def test_file_multiple_validation(self):
258+ f = MultipleFileField(validators=[validate_image_file_extension])
259+
260+ good_files = [
261+ SimpleUploadedFile("image1.jpg", b"fake JPEG"),
262+ SimpleUploadedFile("image2.png", b"faux image"),
263+ SimpleUploadedFile("image3.bmp", b"fraudulent bitmap"),
264+ ]
265+ self.assertEqual(f.clean(good_files), good_files)
266+
267+ evil_files = [
268+ SimpleUploadedFile("image1.sh", b"#!/bin/bash -c 'echo pwned!'\n"),
269+ SimpleUploadedFile("image2.png", b"faux image"),
270+ SimpleUploadedFile("image3.jpg", b"fake JPEG"),
271+ ]
272+
273+ evil_rotations = (
274+ evil_files[i:] + evil_files[:i] # Rotate by i.
275+ for i in range(len(evil_files))
276+ )
277+ msg = "File extension “sh” is not allowed. Allowed extensions are: "
278+ for rotated_evil_files in evil_rotations:
279+ with self.assertRaisesMessage(ValidationError, msg):
280+ f.clean(rotated_evil_files)
281diff --git a/tests/forms_tests/widget_tests/test_clearablefileinput.py b/tests/forms_tests/widget_tests/test_clearablefileinput.py
282index 2ba376d..8d9e38a 100644
283--- a/tests/forms_tests/widget_tests/test_clearablefileinput.py
284+++ b/tests/forms_tests/widget_tests/test_clearablefileinput.py
285@@ -161,3 +161,8 @@ class ClearableFileInputTest(WidgetTest):
286 self.assertIs(widget.value_omitted_from_data({}, {}, 'field'), True)
287 self.assertIs(widget.value_omitted_from_data({}, {'field': 'x'}, 'field'), False)
288 self.assertIs(widget.value_omitted_from_data({'field-clear': 'y'}, {}, 'field'), False)
289+
290+ def test_multiple_error(self):
291+ msg = "ClearableFileInput doesn't support uploading multiple files."
292+ with self.assertRaisesMessage(ValueError, msg):
293+ ClearableFileInput(attrs={"multiple": True})
294diff --git a/tests/forms_tests/widget_tests/test_fileinput.py b/tests/forms_tests/widget_tests/test_fileinput.py
295index bbd7c7f..24daf5d 100644
296--- a/tests/forms_tests/widget_tests/test_fileinput.py
297+++ b/tests/forms_tests/widget_tests/test_fileinput.py
298@@ -1,4 +1,6 @@
299+from django.core.files.uploadedfile import SimpleUploadedFile
300 from django.forms import FileInput
301+from django.utils.datastructures import MultiValueDict
302
303 from .base import WidgetTest
304
305@@ -18,3 +20,45 @@ class FileInputTest(WidgetTest):
306 def test_value_omitted_from_data(self):
307 self.assertIs(self.widget.value_omitted_from_data({}, {}, 'field'), True)
308 self.assertIs(self.widget.value_omitted_from_data({}, {'field': 'value'}, 'field'), False)
309+
310+ def test_multiple_error(self):
311+ msg = "FileInput doesn't support uploading multiple files."
312+ with self.assertRaisesMessage(ValueError, msg):
313+ FileInput(attrs={"multiple": True})
314+
315+ def test_value_from_datadict_multiple(self):
316+ class MultipleFileInput(FileInput):
317+ allow_multiple_selected = True
318+
319+ file_1 = SimpleUploadedFile("something1.txt", b"content 1")
320+ file_2 = SimpleUploadedFile("something2.txt", b"content 2")
321+ # Uploading multiple files is allowed.
322+ widget = MultipleFileInput(attrs={"multiple": True})
323+ value = widget.value_from_datadict(
324+ data={"name": "Test name"},
325+ files=MultiValueDict({"myfile": [file_1, file_2]}),
326+ name="myfile",
327+ )
328+ self.assertEqual(value, [file_1, file_2])
329+ # Uploading multiple files is not allowed.
330+ widget = FileInput()
331+ value = widget.value_from_datadict(
332+ data={"name": "Test name"},
333+ files=MultiValueDict({"myfile": [file_1, file_2]}),
334+ name="myfile",
335+ )
336+ self.assertEqual(value, file_2)
337+
338+ def test_multiple_default(self):
339+ class MultipleFileInput(FileInput):
340+ allow_multiple_selected = True
341+
342+ tests = [
343+ (None, True),
344+ ({"class": "myclass"}, True),
345+ ({"multiple": False}, False),
346+ ]
347+ for attrs, expected in tests:
348+ with self.subTest(attrs=attrs):
349+ widget = MultipleFileInput(attrs=attrs)
350+ self.assertIs(widget.attrs["multiple"], expected)
351--
3522.40.0
diff --git a/meta-python/recipes-devtools/python/python3-django_2.2.28.bb b/meta-python/recipes-devtools/python/python3-django_2.2.28.bb
index 9ef988176e..77a1a2a740 100644
--- a/meta-python/recipes-devtools/python/python3-django_2.2.28.bb
+++ b/meta-python/recipes-devtools/python/python3-django_2.2.28.bb
@@ -5,6 +5,8 @@ UPSTREAM_CHECK_REGEX = "/${PYPI_PACKAGE}/(?P<pver>(2\.2\.\d*)+)/"
5 5
6inherit setuptools3 6inherit setuptools3
7 7
8SRC_URI += "file://CVE-2023-31047.patch"
9
8SRC_URI[sha256sum] = "0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413" 10SRC_URI[sha256sum] = "0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413"
9 11
10RDEPENDS:${PN} += "\ 12RDEPENDS:${PN} += "\