A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/django/django/commit/eed53d0011622e70b936e203005f0e6f4ac48965 below:

[3.2.x] Fixed CVE-2023-31047, Fixed #31710 -- Prevented potential byp… · django/django@eed53d0 · GitHub

File tree Expand file treeCollapse file tree 6 files changed

+215

-9

lines changed

Filter options

Expand file treeCollapse file tree 6 files changed

+215

-9

lines changed Original file line number Diff line number Diff line change

@@ -378,16 +378,40 @@ def format_value(self, value):

378 378 379 379

class FileInput(Input):

380 380

input_type = 'file'

381 +

allow_multiple_selected = False

381 382

needs_multipart_form = True

382 383

template_name = 'django/forms/widgets/file.html'

383 384 385 +

def __init__(self, attrs=None):

386 +

if (

387 +

attrs is not None and

388 +

not self.allow_multiple_selected and

389 +

attrs.get("multiple", False)

390 +

):

391 +

raise ValueError(

392 +

"%s doesn't support uploading multiple files."

393 +

% self.__class__.__qualname__

394 +

)

395 +

if self.allow_multiple_selected:

396 +

if attrs is None:

397 +

attrs = {"multiple": True}

398 +

else:

399 +

attrs.setdefault("multiple", True)

400 +

super().__init__(attrs)

401 + 384 402

def format_value(self, value):

385 403

"""File input never renders a value."""

386 404

return

387 405 388 406

def value_from_datadict(self, data, files, name):

389 407

"File widgets take data from FILES, not POST"

390 -

return files.get(name)

408 +

getter = files.get

409 +

if self.allow_multiple_selected:

410 +

try:

411 +

getter = files.getlist

412 +

except AttributeError:

413 +

pass

414 +

return getter(name)

391 415 392 416

def value_omitted_from_data(self, data, files, name):

393 417

return name not in files

Original file line number Diff line number Diff line change

@@ -6,4 +6,18 @@ Django 3.2.19 release notes

6 6 7 7

Django 3.2.19 fixes a security issue with severity "low" in 3.2.18.

8 8 9 -

...

9 +

CVE-2023-31047: Potential bypass of validation when uploading multiple files using one form field

10 +

=================================================================================================

11 + 12 +

Uploading multiple files using one form field has never been supported by

13 +

:class:`.forms.FileField` or :class:`.forms.ImageField` as only the last

14 +

uploaded file was validated. Unfortunately, :ref:`uploading_multiple_files`

15 +

topic suggested otherwise.

16 + 17 +

In order to avoid the vulnerability, :class:`~django.forms.ClearableFileInput`

18 +

and :class:`~django.forms.FileInput` form widgets now raise ``ValueError`` when

19 +

the ``multiple`` HTML attribute is set on them. To prevent the exception and

20 +

keep the old behavior, set ``allow_multiple_selected`` to ``True``.

21 + 22 +

For more details on using the new attribute and handling of multiple files

23 +

through a single field, see :ref:`uploading_multiple_files`.

Original file line number Diff line number Diff line change

@@ -126,19 +126,54 @@ model::

126 126

form = UploadFileForm()

127 127

return render(request, 'upload.html', {'form': form})

128 128 129 +

.. _uploading_multiple_files:

130 + 129 131

Uploading multiple files

130 132

------------------------

131 133 132 -

If you want to upload multiple files using one form field, set the ``multiple``

133 -

HTML attribute of field's widget:

134 +

..

135 +

Tests in tests.forms_tests.field_tests.test_filefield.MultipleFileFieldTest

136 +

should be updated after any changes in the following snippets.

137 + 138 +

If you want to upload multiple files using one form field, create a subclass

139 +

of the field's widget and set the ``allow_multiple_selected`` attribute on it

140 +

to ``True``.

141 + 142 +

In order for such files to be all validated by your form (and have the value of

143 +

the field include them all), you will also have to subclass ``FileField``. See

144 +

below for an example.

145 + 146 +

.. admonition:: Multiple file field

147 + 148 +

Django is likely to have a proper multiple file field support at some point

149 +

in the future.

134 150 135 151

.. code-block:: python

136 152

:caption: ``forms.py``

137 153 138 154

from django import forms

139 155 156 + 157 +

class MultipleFileInput(forms.ClearableFileInput):

158 +

allow_multiple_selected = True

159 + 160 + 161 +

class MultipleFileField(forms.FileField):

162 +

def __init__(self, *args, **kwargs):

163 +

kwargs.setdefault("widget", MultipleFileInput())

164 +

super().__init__(*args, **kwargs)

165 + 166 +

def clean(self, data, initial=None):

167 +

single_file_clean = super().clean

168 +

if isinstance(data, (list, tuple)):

169 +

result = [single_file_clean(d, initial) for d in data]

170 +

else:

171 +

result = single_file_clean(data, initial)

172 +

return result

173 + 174 + 140 175

class FileFieldForm(forms.Form):

141 -

file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

176 +

file_field = MultipleFileField()

142 177 143 178

Then override the ``post`` method of your

144 179

:class:`~django.views.generic.edit.FormView` subclass to handle multiple file

@@ -158,14 +193,32 @@ uploads:

158 193

def post(self, request, *args, **kwargs):

159 194

form_class = self.get_form_class()

160 195

form = self.get_form(form_class)

161 -

files = request.FILES.getlist('file_field')

162 196

if form.is_valid():

163 -

for f in files:

164 -

... # Do something with each file.

165 197

return self.form_valid(form)

166 198

else:

167 199

return self.form_invalid(form)

168 200 201 +

def form_valid(self, form):

202 +

files = form.cleaned_data["file_field"]

203 +

for f in files:

204 +

... # Do something with each file.

205 +

return super().form_valid()

206 + 207 +

.. warning::

208 + 209 +

This will allow you to handle multiple files at the form level only. Be

210 +

aware that you cannot use it to put multiple files on a single model

211 +

instance (in a single field), for example, even if the custom widget is used

212 +

with a form field related to a model ``FileField``.

213 + 214 +

.. versionchanged:: 3.2.19

215 + 216 +

In previous versions, there was no support for the ``allow_multiple_selected``

217 +

class attribute, and users were advised to create the widget with the HTML

218 +

attribute ``multiple`` set through the ``attrs`` argument. However, this

219 +

caused validation of the form field to be applied only to the last file

220 +

submitted, which could have adverse security implications.

221 + 169 222

Upload Handlers

170 223

===============

171 224 Original file line number Diff line number Diff line change

@@ -2,7 +2,8 @@

2 2 3 3

from django.core.exceptions import ValidationError

4 4

from django.core.files.uploadedfile import SimpleUploadedFile

5 -

from django.forms import FileField

5 +

from django.core.validators import validate_image_file_extension

6 +

from django.forms import FileField, FileInput

6 7

from django.test import SimpleTestCase

7 8 8 9

@@ -83,3 +84,68 @@ def test_disabled_has_changed(self):

83 84 84 85

def test_file_picklable(self):

85 86

self.assertIsInstance(pickle.loads(pickle.dumps(FileField())), FileField)

87 + 88 + 89 +

class MultipleFileInput(FileInput):

90 +

allow_multiple_selected = True

91 + 92 + 93 +

class MultipleFileField(FileField):

94 +

def __init__(self, *args, **kwargs):

95 +

kwargs.setdefault("widget", MultipleFileInput())

96 +

super().__init__(*args, **kwargs)

97 + 98 +

def clean(self, data, initial=None):

99 +

single_file_clean = super().clean

100 +

if isinstance(data, (list, tuple)):

101 +

result = [single_file_clean(d, initial) for d in data]

102 +

else:

103 +

result = single_file_clean(data, initial)

104 +

return result

105 + 106 + 107 +

class MultipleFileFieldTest(SimpleTestCase):

108 +

def test_file_multiple(self):

109 +

f = MultipleFileField()

110 +

files = [

111 +

SimpleUploadedFile("name1", b"Content 1"),

112 +

SimpleUploadedFile("name2", b"Content 2"),

113 +

]

114 +

self.assertEqual(f.clean(files), files)

115 + 116 +

def test_file_multiple_empty(self):

117 +

f = MultipleFileField()

118 +

files = [

119 +

SimpleUploadedFile("empty", b""),

120 +

SimpleUploadedFile("nonempty", b"Some Content"),

121 +

]

122 +

msg = "'The submitted file is empty.'"

123 +

with self.assertRaisesMessage(ValidationError, msg):

124 +

f.clean(files)

125 +

with self.assertRaisesMessage(ValidationError, msg):

126 +

f.clean(files[::-1])

127 + 128 +

def test_file_multiple_validation(self):

129 +

f = MultipleFileField(validators=[validate_image_file_extension])

130 + 131 +

good_files = [

132 +

SimpleUploadedFile("image1.jpg", b"fake JPEG"),

133 +

SimpleUploadedFile("image2.png", b"faux image"),

134 +

SimpleUploadedFile("image3.bmp", b"fraudulent bitmap"),

135 +

]

136 +

self.assertEqual(f.clean(good_files), good_files)

137 + 138 +

evil_files = [

139 +

SimpleUploadedFile("image1.sh", b"#!/bin/bash -c 'echo pwned!'\n"),

140 +

SimpleUploadedFile("image2.png", b"faux image"),

141 +

SimpleUploadedFile("image3.jpg", b"fake JPEG"),

142 +

]

143 + 144 +

evil_rotations = (

145 +

evil_files[i:] + evil_files[:i] # Rotate by i.

146 +

for i in range(len(evil_files))

147 +

)

148 +

msg = "File extension “sh” is not allowed. Allowed extensions are: "

149 +

for rotated_evil_files in evil_rotations:

150 +

with self.assertRaisesMessage(ValidationError, msg):

151 +

f.clean(rotated_evil_files)

Original file line number Diff line number Diff line change

@@ -176,3 +176,8 @@ def test_value_omitted_from_data(self):

176 176

self.assertIs(widget.value_omitted_from_data({}, {}, 'field'), True)

177 177

self.assertIs(widget.value_omitted_from_data({}, {'field': 'x'}, 'field'), False)

178 178

self.assertIs(widget.value_omitted_from_data({'field-clear': 'y'}, {}, 'field'), False)

179 + 180 +

def test_multiple_error(self):

181 +

msg = "ClearableFileInput doesn't support uploading multiple files."

182 +

with self.assertRaisesMessage(ValueError, msg):

183 +

ClearableFileInput(attrs={"multiple": True})

Original file line number Diff line number Diff line change

@@ -1,4 +1,6 @@

1 +

from django.core.files.uploadedfile import SimpleUploadedFile

1 2

from django.forms import FileInput

3 +

from django.utils.datastructures import MultiValueDict

2 4 3 5

from .base import WidgetTest

4 6

@@ -24,3 +26,45 @@ def test_use_required_attribute(self):

24 26

# user to keep the existing, initial value.

25 27

self.assertIs(self.widget.use_required_attribute(None), True)

26 28

self.assertIs(self.widget.use_required_attribute('resume.txt'), False)

29 + 30 +

def test_multiple_error(self):

31 +

msg = "FileInput doesn't support uploading multiple files."

32 +

with self.assertRaisesMessage(ValueError, msg):

33 +

FileInput(attrs={"multiple": True})

34 + 35 +

def test_value_from_datadict_multiple(self):

36 +

class MultipleFileInput(FileInput):

37 +

allow_multiple_selected = True

38 + 39 +

file_1 = SimpleUploadedFile("something1.txt", b"content 1")

40 +

file_2 = SimpleUploadedFile("something2.txt", b"content 2")

41 +

# Uploading multiple files is allowed.

42 +

widget = MultipleFileInput(attrs={"multiple": True})

43 +

value = widget.value_from_datadict(

44 +

data={"name": "Test name"},

45 +

files=MultiValueDict({"myfile": [file_1, file_2]}),

46 +

name="myfile",

47 +

)

48 +

self.assertEqual(value, [file_1, file_2])

49 +

# Uploading multiple files is not allowed.

50 +

widget = FileInput()

51 +

value = widget.value_from_datadict(

52 +

data={"name": "Test name"},

53 +

files=MultiValueDict({"myfile": [file_1, file_2]}),

54 +

name="myfile",

55 +

)

56 +

self.assertEqual(value, file_2)

57 + 58 +

def test_multiple_default(self):

59 +

class MultipleFileInput(FileInput):

60 +

allow_multiple_selected = True

61 + 62 +

tests = [

63 +

(None, True),

64 +

({"class": "myclass"}, True),

65 +

({"multiple": False}, False),

66 +

]

67 +

for attrs, expected in tests:

68 +

with self.subTest(attrs=attrs):

69 +

widget = MultipleFileInput(attrs=attrs)

70 +

self.assertIs(widget.attrs["multiple"], expected)

You can’t perform that action at this time.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4