A RetroSearch Logo

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

Search Query:

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

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

File tree Expand file treeCollapse file tree 7 files changed

+229

-10

lines changed

Filter options

Expand file treeCollapse file tree 7 files changed

+229

-10

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

@@ -413,17 +413,41 @@ def format_value(self, value):

413 413 414 414 415 415

class FileInput(Input):

416 +

allow_multiple_selected = False

416 417

input_type = "file"

417 418

needs_multipart_form = True

418 419

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

419 420 421 +

def __init__(self, attrs=None):

422 +

if (

423 +

attrs is not None

424 +

and not self.allow_multiple_selected

425 +

and attrs.get("multiple", False)

426 +

):

427 +

raise ValueError(

428 +

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

429 +

% self.__class__.__qualname__

430 +

)

431 +

if self.allow_multiple_selected:

432 +

if attrs is None:

433 +

attrs = {"multiple": True}

434 +

else:

435 +

attrs.setdefault("multiple", True)

436 +

super().__init__(attrs)

437 + 420 438

def format_value(self, value):

421 439

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

422 440

return

423 441 424 442

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

425 443

"File widgets take data from FILES, not POST"

426 -

return files.get(name)

444 +

getter = files.get

445 +

if self.allow_multiple_selected:

446 +

try:

447 +

getter = files.getlist

448 +

except AttributeError:

449 +

pass

450 +

return getter(name)

427 451 428 452

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

429 453

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

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

6 6 7 7

Django 4.1.9 fixes a security issue with severity "low" in 4.1.8.

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

@@ -139,19 +139,53 @@ a :class:`~django.core.files.File` like object to the

139 139

instance = ModelWithFileField(file_field=content_file)

140 140

instance.save()

141 141 142 +

.. _uploading_multiple_files:

143 + 142 144

Uploading multiple files

143 145

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

144 146 145 -

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

146 -

HTML attribute of field's widget:

147 +

..

148 +

Tests in tests.forms_tests.field_tests.test_filefield.MultipleFileFieldTest

149 +

should be updated after any changes in the following snippets.

150 + 151 +

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

152 +

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

153 +

to ``True``.

154 + 155 +

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

156 +

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

157 +

below for an example.

158 + 159 +

.. admonition:: Multiple file field

160 + 161 +

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

162 +

in the future.

147 163 148 164

.. code-block:: python

149 165

:caption: ``forms.py``

150 166 151 167

from django import forms

152 168 169 +

class MultipleFileInput(forms.ClearableFileInput):

170 +

allow_multiple_selected = True

171 + 172 + 173 +

class MultipleFileField(forms.FileField):

174 +

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

175 +

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

176 +

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

177 + 178 +

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

179 +

single_file_clean = super().clean

180 +

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

181 +

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

182 +

else:

183 +

result = single_file_clean(data, initial)

184 +

return result

185 + 186 + 153 187

class FileFieldForm(forms.Form):

154 -

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

188 +

file_field = MultipleFileField()

155 189 156 190

Then override the ``post`` method of your

157 191

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

@@ -171,14 +205,32 @@ uploads:

171 205

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

172 206

form_class = self.get_form_class()

173 207

form = self.get_form(form_class)

174 -

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

175 208

if form.is_valid():

176 -

for f in files:

177 -

... # Do something with each file.

178 209

return self.form_valid(form)

179 210

else:

180 211

return self.form_invalid(form)

181 212 213 +

def form_valid(self, form):

214 +

files = form.cleaned_data["file_field"]

215 +

for f in files:

216 +

... # Do something with each file.

217 +

return super().form_valid()

218 + 219 +

.. warning::

220 + 221 +

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

222 +

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

223 +

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

224 +

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

225 + 226 +

.. versionchanged:: 3.2.19

227 + 228 +

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

229 +

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

230 +

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

231 +

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

232 +

submitted, which could have adverse security implications.

233 + 182 234

Upload Handlers

183 235

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

184 236 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

@@ -109,3 +110,68 @@ def test_disabled_has_changed(self):

109 110 110 111

def test_file_picklable(self):

111 112

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

113 + 114 + 115 +

class MultipleFileInput(FileInput):

116 +

allow_multiple_selected = True

117 + 118 + 119 +

class MultipleFileField(FileField):

120 +

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

121 +

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

122 +

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

123 + 124 +

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

125 +

single_file_clean = super().clean

126 +

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

127 +

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

128 +

else:

129 +

result = single_file_clean(data, initial)

130 +

return result

131 + 132 + 133 +

class MultipleFileFieldTest(SimpleTestCase):

134 +

def test_file_multiple(self):

135 +

f = MultipleFileField()

136 +

files = [

137 +

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

138 +

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

139 +

]

140 +

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

141 + 142 +

def test_file_multiple_empty(self):

143 +

f = MultipleFileField()

144 +

files = [

145 +

SimpleUploadedFile("empty", b""),

146 +

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

147 +

]

148 +

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

149 +

with self.assertRaisesMessage(ValidationError, msg):

150 +

f.clean(files)

151 +

with self.assertRaisesMessage(ValidationError, msg):

152 +

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

153 + 154 +

def test_file_multiple_validation(self):

155 +

f = MultipleFileField(validators=[validate_image_file_extension])

156 + 157 +

good_files = [

158 +

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

159 +

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

160 +

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

161 +

]

162 +

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

163 + 164 +

evil_files = [

165 +

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

166 +

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

167 +

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

168 +

]

169 + 170 +

evil_rotations = (

171 +

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

172 +

for i in range(len(evil_files))

173 +

)

174 +

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

175 +

for rotated_evil_files in evil_rotations:

176 +

with self.assertRaisesMessage(ValidationError, msg):

177 +

f.clean(rotated_evil_files)

Original file line number Diff line number Diff line change

@@ -232,3 +232,8 @@ class TestForm(Form):

232 232

'<input type="file" name="clearable_file" id="id_clearable_file"></div>',

233 233

form.render(),

234 234

)

235 + 236 +

def test_multiple_error(self):

237 +

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

238 +

with self.assertRaisesMessage(ValueError, msg):

239 +

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 FileField, FileInput, Form

3 +

from django.utils.datastructures import MultiValueDict

2 4 3 5

from .base import WidgetTest

4 6

@@ -48,3 +50,45 @@ class TestForm(Form):

48 50

'name="field" required type="file"></div>',

49 51

form.render(),

50 52

)

53 + 54 +

def test_multiple_error(self):

55 +

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

56 +

with self.assertRaisesMessage(ValueError, msg):

57 +

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

58 + 59 +

def test_value_from_datadict_multiple(self):

60 +

class MultipleFileInput(FileInput):

61 +

allow_multiple_selected = True

62 + 63 +

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

64 +

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

65 +

# Uploading multiple files is allowed.

66 +

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

67 +

value = widget.value_from_datadict(

68 +

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

69 +

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

70 +

name="myfile",

71 +

)

72 +

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

73 +

# Uploading multiple files is not allowed.

74 +

widget = FileInput()

75 +

value = widget.value_from_datadict(

76 +

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

77 +

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

78 +

name="myfile",

79 +

)

80 +

self.assertEqual(value, file_2)

81 + 82 +

def test_multiple_default(self):

83 +

class MultipleFileInput(FileInput):

84 +

allow_multiple_selected = True

85 + 86 +

tests = [

87 +

(None, True),

88 +

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

89 +

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

90 +

]

91 +

for attrs, expected in tests:

92 +

with self.subTest(attrs=attrs):

93 +

widget = MultipleFileInput(attrs=attrs)

94 +

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