diff -Nru python-django-2.2.22/debian/changelog python-django-2.2.23/debian/changelog
--- python-django-2.2.22/debian/changelog 2021-05-06 14:52:24.000000000 +0000
+++ python-django-2.2.23/debian/changelog 2021-05-13 09:41:04.000000000 +0000
@@ -1,3 +1,10 @@
+python-django (2:2.2.23-1) unstable; urgency=medium
+
+ * New upstream release.
+
+
+ -- Chris Lamb Thu, 13 May 2021 10:41:04 +0100
+
python-django (2:2.2.22-1) unstable; urgency=medium
* New upstream security release:
diff -Nru python-django-2.2.22/django/core/files/utils.py python-django-2.2.23/django/core/files/utils.py
--- python-django-2.2.22/django/core/files/utils.py 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/django/core/files/utils.py 2021-05-13 07:19:37.000000000 +0000
@@ -1,16 +1,26 @@
import os
+import pathlib
from django.core.exceptions import SuspiciousFileOperation
-def validate_file_name(name):
- if name != os.path.basename(name):
- raise SuspiciousFileOperation("File name '%s' includes path elements" % name)
-
+def validate_file_name(name, allow_relative_path=False):
# Remove potentially dangerous names
- if name in {'', '.', '..'}:
+ if os.path.basename(name) in {'', '.', '..'}:
raise SuspiciousFileOperation("Could not derive file name from '%s'" % name)
+ if allow_relative_path:
+ # Use PurePosixPath() because this branch is checked only in
+ # FileField.generate_filename() where all file paths are expected to be
+ # Unix style (with forward slashes).
+ path = pathlib.PurePosixPath(name)
+ if path.is_absolute() or '..' in path.parts:
+ raise SuspiciousFileOperation(
+ "Detected path traversal attempt in '%s'" % name
+ )
+ elif name != os.path.basename(name):
+ raise SuspiciousFileOperation("File name '%s' includes path elements" % name)
+
return name
diff -Nru python-django-2.2.22/django/db/models/fields/files.py python-django-2.2.23/django/db/models/fields/files.py
--- python-django-2.2.22/django/db/models/fields/files.py 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/django/db/models/fields/files.py 2021-05-13 07:19:37.000000000 +0000
@@ -300,12 +300,12 @@
Until the storage layer, all file paths are expected to be Unix style
(with forward slashes).
"""
- filename = validate_file_name(filename)
if callable(self.upload_to):
filename = self.upload_to(instance, filename)
else:
dirname = datetime.datetime.now().strftime(self.upload_to)
filename = posixpath.join(dirname, filename)
+ filename = validate_file_name(filename, allow_relative_path=True)
return self.storage.generate_filename(filename)
def save_form_data(self, instance, data):
diff -Nru python-django-2.2.22/django/__init__.py python-django-2.2.23/django/__init__.py
--- python-django-2.2.22/django/__init__.py 2021-05-06 07:08:06.000000000 +0000
+++ python-django-2.2.23/django/__init__.py 2021-05-13 07:19:51.000000000 +0000
@@ -1,6 +1,6 @@
from django.utils.version import get_version
-VERSION = (2, 2, 22, 'final', 0)
+VERSION = (2, 2, 23, 'final', 0)
__version__ = get_version(VERSION)
diff -Nru python-django-2.2.22/Django.egg-info/PKG-INFO python-django-2.2.23/Django.egg-info/PKG-INFO
--- python-django-2.2.22/Django.egg-info/PKG-INFO 2021-05-06 07:09:20.000000000 +0000
+++ python-django-2.2.23/Django.egg-info/PKG-INFO 2021-05-13 07:20:45.000000000 +0000
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: Django
-Version: 2.2.22
+Version: 2.2.23
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
diff -Nru python-django-2.2.22/Django.egg-info/SOURCES.txt python-django-2.2.23/Django.egg-info/SOURCES.txt
--- python-django-2.2.22/Django.egg-info/SOURCES.txt 2021-05-06 07:09:21.000000000 +0000
+++ python-django-2.2.23/Django.egg-info/SOURCES.txt 2021-05-13 07:20:46.000000000 +0000
@@ -3832,6 +3832,7 @@
docs/releases/2.2.20.txt
docs/releases/2.2.21.txt
docs/releases/2.2.22.txt
+docs/releases/2.2.23.txt
docs/releases/2.2.3.txt
docs/releases/2.2.4.txt
docs/releases/2.2.5.txt
diff -Nru python-django-2.2.22/docs/releases/2.2.21.txt python-django-2.2.23/docs/releases/2.2.21.txt
--- python-django-2.2.22/docs/releases/2.2.21.txt 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/docs/releases/2.2.21.txt 2021-05-13 07:19:37.000000000 +0000
@@ -13,5 +13,4 @@
directory-traversal via uploaded files with suitably crafted file names.
In order to mitigate this risk, stricter basename and path sanitation is now
-applied. Specifically, empty file names and paths with dot segments will be
-rejected.
+applied.
diff -Nru python-django-2.2.22/docs/releases/2.2.23.txt python-django-2.2.23/docs/releases/2.2.23.txt
--- python-django-2.2.22/docs/releases/2.2.23.txt 1970-01-01 00:00:00.000000000 +0000
+++ python-django-2.2.23/docs/releases/2.2.23.txt 2021-05-13 07:19:37.000000000 +0000
@@ -0,0 +1,15 @@
+===========================
+Django 2.2.23 release notes
+===========================
+
+*May 13, 2021*
+
+Django 2.2.23 fixes a regression in 2.2.21.
+
+Bugfixes
+========
+
+* Fixed a regression in Django 2.2.21 where saving ``FileField`` would raise a
+ ``SuspiciousFileOperation`` even when a custom
+ :attr:`~django.db.models.FileField.upload_to` returns a valid file path
+ (:ticket:`32718`).
diff -Nru python-django-2.2.22/docs/releases/index.txt python-django-2.2.23/docs/releases/index.txt
--- python-django-2.2.22/docs/releases/index.txt 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/docs/releases/index.txt 2021-05-13 07:19:37.000000000 +0000
@@ -25,6 +25,7 @@
.. toctree::
:maxdepth: 1
+ 2.2.23
2.2.22
2.2.21
2.2.20
diff -Nru python-django-2.2.22/docs/releases/security.txt python-django-2.2.23/docs/releases/security.txt
--- python-django-2.2.22/docs/releases/security.txt 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/docs/releases/security.txt 2021-05-13 07:19:37.000000000 +0000
@@ -1189,3 +1189,17 @@
* Django 3.2 :commit:`(patch) `
* Django 3.1 :commit:`(patch) <25d84d64122c15050a0ee739e859f22ddab5ac48>`
* Django 2.2 :commit:`(patch) <04ac1624bdc2fa737188401757cf95ced122d26d>`
+
+May 6, 2021 - :cve:`2021-32052`
+-------------------------------
+
+Header injection possibility since ``URLValidator`` accepted newlines in input
+on Python 3.9.5+. `Full description
+`__
+
+Versions affected
+~~~~~~~~~~~~~~~~~
+
+* Django 3.2 :commit:`(patch) <2d2c1d0c97832860fbd6597977e2aae17dd7e5b2>`
+* Django 3.1 :commit:`(patch) `
+* Django 2.2 :commit:`(patch) `
diff -Nru python-django-2.2.22/PKG-INFO python-django-2.2.23/PKG-INFO
--- python-django-2.2.22/PKG-INFO 2021-05-06 07:09:21.533331900 +0000
+++ python-django-2.2.23/PKG-INFO 2021-05-13 07:20:46.554731100 +0000
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: Django
-Version: 2.2.22
+Version: 2.2.23
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
diff -Nru python-django-2.2.22/tests/file_storage/test_generate_filename.py python-django-2.2.23/tests/file_storage/test_generate_filename.py
--- python-django-2.2.22/tests/file_storage/test_generate_filename.py 2021-05-06 07:07:46.000000000 +0000
+++ python-django-2.2.23/tests/file_storage/test_generate_filename.py 2021-05-13 07:19:37.000000000 +0000
@@ -1,6 +1,4 @@
import os
-import sys
-from unittest import skipIf
from django.core.exceptions import SuspiciousFileOperation
from django.core.files.base import ContentFile
@@ -64,19 +62,37 @@
s.generate_filename(file_name)
def test_filefield_dangerous_filename(self):
- candidates = ['..', '.', '', '???', '$.$.$']
+ candidates = [
+ ('..', 'some/folder/..'),
+ ('.', 'some/folder/.'),
+ ('', 'some/folder/'),
+ ('???', '???'),
+ ('$.$.$', '$.$.$'),
+ ]
f = FileField(upload_to='some/folder/')
- msg = "Could not derive file name from '%s'"
- for file_name in candidates:
+ for file_name, msg_file_name in candidates:
+ msg = f"Could not derive file name from '{msg_file_name}'"
with self.subTest(file_name=file_name):
- with self.assertRaisesMessage(SuspiciousFileOperation, msg % file_name):
+ with self.assertRaisesMessage(SuspiciousFileOperation, msg):
f.generate_filename(None, file_name)
- def test_filefield_dangerous_filename_dir(self):
+ def test_filefield_dangerous_filename_dot_segments(self):
f = FileField(upload_to='some/folder/')
- msg = "File name '/tmp/path' includes path elements"
+ msg = "Detected path traversal attempt in 'some/folder/../path'"
with self.assertRaisesMessage(SuspiciousFileOperation, msg):
- f.generate_filename(None, '/tmp/path')
+ f.generate_filename(None, '../path')
+
+ def test_filefield_generate_filename_absolute_path(self):
+ f = FileField(upload_to='some/folder/')
+ candidates = [
+ '/tmp/path',
+ '/tmp/../path',
+ ]
+ for file_name in candidates:
+ msg = f"Detected path traversal attempt in '{file_name}'"
+ with self.subTest(file_name=file_name):
+ with self.assertRaisesMessage(SuspiciousFileOperation, msg):
+ f.generate_filename(None, file_name)
def test_filefield_generate_filename(self):
f = FileField(upload_to='some/folder/')
@@ -95,7 +111,57 @@
os.path.normpath('some/folder/test_with_space.txt')
)
- @skipIf(sys.platform == 'win32', 'Path components in filename are not supported after 0b79eb3.')
+ def test_filefield_generate_filename_upload_to_overrides_dangerous_filename(self):
+ def upload_to(instance, filename):
+ return 'test.txt'
+
+ f = FileField(upload_to=upload_to)
+ candidates = [
+ '/tmp/.',
+ '/tmp/..',
+ '/tmp/../path',
+ '/tmp/path',
+ 'some/folder/',
+ 'some/folder/.',
+ 'some/folder/..',
+ 'some/folder/???',
+ 'some/folder/$.$.$',
+ 'some/../test.txt',
+ '',
+ ]
+ for file_name in candidates:
+ with self.subTest(file_name=file_name):
+ self.assertEqual(f.generate_filename(None, file_name), 'test.txt')
+
+ def test_filefield_generate_filename_upload_to_absolute_path(self):
+ def upload_to(instance, filename):
+ return '/tmp/' + filename
+
+ f = FileField(upload_to=upload_to)
+ candidates = [
+ 'path',
+ '../path',
+ '???',
+ '$.$.$',
+ ]
+ for file_name in candidates:
+ msg = f"Detected path traversal attempt in '/tmp/{file_name}'"
+ with self.subTest(file_name=file_name):
+ with self.assertRaisesMessage(SuspiciousFileOperation, msg):
+ f.generate_filename(None, file_name)
+
+ def test_filefield_generate_filename_upload_to_dangerous_filename(self):
+ def upload_to(instance, filename):
+ return '/tmp/' + filename
+
+ f = FileField(upload_to=upload_to)
+ candidates = ['..', '.', '']
+ for file_name in candidates:
+ msg = f"Could not derive file name from '/tmp/{file_name}'"
+ with self.subTest(file_name=file_name):
+ with self.assertRaisesMessage(SuspiciousFileOperation, msg):
+ f.generate_filename(None, file_name)
+
def test_filefield_awss3_storage(self):
"""
Simulate a FileField with an S3 storage which uses keys rather than
diff -Nru python-django-2.2.22/tests/model_fields/test_filefield.py python-django-2.2.23/tests/model_fields/test_filefield.py
--- python-django-2.2.22/tests/model_fields/test_filefield.py 2021-05-06 07:07:43.000000000 +0000
+++ python-django-2.2.23/tests/model_fields/test_filefield.py 2021-05-13 07:19:37.000000000 +0000
@@ -1,8 +1,10 @@
import os
import sys
+import tempfile
import unittest
-from django.core.files import temp
+from django.core.exceptions import SuspiciousFileOperation
+from django.core.files import File, temp
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import TemporaryUploadedFile
from django.db.utils import IntegrityError
@@ -59,6 +61,15 @@
d.refresh_from_db()
self.assertIs(d.myfile.instance, d)
+ @unittest.skipIf(sys.platform == 'win32', "Crashes with OSError on Windows.")
+ def test_save_without_name(self):
+ with tempfile.NamedTemporaryFile(suffix='.txt') as tmp:
+ document = Document.objects.create(myfile='something.txt')
+ document.myfile = File(tmp)
+ msg = f"Detected path traversal attempt in '{tmp.name}'"
+ with self.assertRaisesMessage(SuspiciousFileOperation, msg):
+ document.save()
+
def test_defer(self):
Document.objects.create(myfile='something.txt')
self.assertEqual(Document.objects.defer('myfile')[0].myfile, 'something.txt')