Package: release.debian.org Severity: normal Tags: buster User: release.debian....@packages.debian.org Usertags: pu
Dear stable release managers, Please consider python-django (1:1.11.29-1+deb10u1) for buster: python-django (1:1.11.29-1+deb10u1) buster; urgency=high . * CVE-2021-23336: Prevent a web cache poisoning attack via "parameter cloaking". Django contains a copy of urllib.parse.parse_qsl() which was added to backport some security fixes. A further security fix has been issued recently such that parse_qsl() no longer allows using ";" as a query parameter separator by default. (Closes: #983090) . For more information, please see: . https://www.djangoproject.com/weblog/2021/feb/19/security-releases/ The full diff is attached. The security team believe this should go via s-p-u rather than via a DLA (if at all): https://bugs.debian.org/983090#27 Please double-check the version number for me. The current version in buster-security is 1:1.11.29-1~deb10u1 (with a tilde). Regards, -- ,''`. : :' : Chris Lamb `. `'` la...@debian.org / chris-lamb.co.uk `-
diff --git a/debian/changelog b/debian/changelog index 00bbc0532..2683a597b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +python-django (1:1.11.29-1+deb10u1) buster; urgency=high + + * CVE-2021-23336: Prevent a web cache poisoning attack via "parameter + cloaking". Django contains a copy of urllib.parse.parse_qsl() which was + added to backport some security fixes. A further security fix has been + issued recently such that parse_qsl() no longer allows using ";" as a + query parameter separator by default. (Closes: #983090) + + For more information, please see: + + https://www.djangoproject.com/weblog/2021/feb/19/security-releases/ + + -- Chris Lamb <la...@debian.org> Thu, 25 Feb 2021 16:37:19 +0000 + python-django (1:1.11.29-1~deb10u1) buster-security; urgency=high * New upstream security release (postponed from March 2020): diff --git a/debian/patches/0010-CVE-2021-23336.patch b/debian/patches/0010-CVE-2021-23336.patch new file mode 100644 index 000000000..192ac7251 --- /dev/null +++ b/debian/patches/0010-CVE-2021-23336.patch @@ -0,0 +1,147 @@ +From: Chris Lamb <la...@debian.org> +Date: Thu, 25 Feb 2021 16:27:58 +0000 +Subject: CVE-2021-23336 + +--- + django/utils/http.py | 2 +- + tests/handlers/test_exception.py | 2 +- + tests/requests/test_data_upload_settings.py | 8 ++--- + tests/utils_tests/test_http.py | 55 +++++++++++++++++++++++++++++ + 4 files changed, 61 insertions(+), 6 deletions(-) + +diff --git a/django/utils/http.py b/django/utils/http.py +index 644d4d0..adeabe9 100644 +--- a/django/utils/http.py ++++ b/django/utils/http.py +@@ -56,7 +56,7 @@ ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y)) + RFC3986_GENDELIMS = str(":/?#[]@") + RFC3986_SUBDELIMS = str("!$&'()*+,;=") + +-FIELDS_MATCH = re.compile('[&;]') ++FIELDS_MATCH = re.compile('&') + + + @keep_lazy_text +diff --git a/tests/handlers/test_exception.py b/tests/handlers/test_exception.py +index 7afd4ac..0c1e763 100644 +--- a/tests/handlers/test_exception.py ++++ b/tests/handlers/test_exception.py +@@ -6,7 +6,7 @@ from django.test.client import FakePayload + class ExceptionHandlerTests(SimpleTestCase): + + def get_suspicious_environ(self): +- payload = FakePayload('a=1&a=2;a=3\r\n') ++ payload = FakePayload('a=1&a=2&a=3\r\n') + return { + 'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', +diff --git a/tests/requests/test_data_upload_settings.py b/tests/requests/test_data_upload_settings.py +index f60f185..44897cc 100644 +--- a/tests/requests/test_data_upload_settings.py ++++ b/tests/requests/test_data_upload_settings.py +@@ -11,7 +11,7 @@ TOO_MUCH_DATA_MSG = 'Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE. + + class DataUploadMaxMemorySizeFormPostTests(SimpleTestCase): + def setUp(self): +- payload = FakePayload('a=1&a=2;a=3\r\n') ++ payload = FakePayload('a=1&a=2&a=3\r\n') + self.request = WSGIRequest({ + 'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', +@@ -117,7 +117,7 @@ class DataUploadMaxNumberOfFieldsGet(SimpleTestCase): + request = WSGIRequest({ + 'REQUEST_METHOD': 'GET', + 'wsgi.input': BytesIO(b''), +- 'QUERY_STRING': 'a=1&a=2;a=3', ++ 'QUERY_STRING': 'a=1&a=2&a=3', + }) + request.GET['a'] + +@@ -126,7 +126,7 @@ class DataUploadMaxNumberOfFieldsGet(SimpleTestCase): + request = WSGIRequest({ + 'REQUEST_METHOD': 'GET', + 'wsgi.input': BytesIO(b''), +- 'QUERY_STRING': 'a=1&a=2;a=3', ++ 'QUERY_STRING': 'a=1&a=2&a=3', + }) + request.GET['a'] + +@@ -168,7 +168,7 @@ class DataUploadMaxNumberOfFieldsMultipartPost(SimpleTestCase): + + class DataUploadMaxNumberOfFieldsFormPost(SimpleTestCase): + def setUp(self): +- payload = FakePayload("\r\n".join(['a=1&a=2;a=3', ''])) ++ payload = FakePayload("\r\n".join(['a=1&a=2&a=3', ''])) + self.request = WSGIRequest({ + 'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', +diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py +index d339e8a..b1184c2 100644 +--- a/tests/utils_tests/test_http.py ++++ b/tests/utils_tests/test_http.py +@@ -5,6 +5,7 @@ import sys + import unittest + from datetime import datetime + ++from django.core.exceptions import TooManyFieldsSent + from django.test import ignore_warnings + from django.utils import http, six + from django.utils.datastructures import MultiValueDict +@@ -258,3 +259,57 @@ class EscapeLeadingSlashesTests(unittest.TestCase): + ) + for url, expected in tests: + self.assertEqual(http.escape_leading_slashes(url), expected) ++ ++ ++# Backport of unit tests for urllib.parse.parse_qsl() from Python 3.8.8. ++# Copyright (C) 2021 Python Software Foundation (see LICENSE.python). ++class ParseQSLBackportTests(unittest.TestCase): ++ def test_parse_qsl(self): ++ tests = [ ++ ('', []), ++ ('&', []), ++ ('&&', []), ++ ('=', [('', '')]), ++ ('=a', [('', 'a')]), ++ ('a', [('a', '')]), ++ ('a=', [('a', '')]), ++ ('&a=b', [('a', 'b')]), ++ ('a=a+b&b=b+c', [('a', 'a b'), ('b', 'b c')]), ++ ('a=1&a=2', [('a', '1'), ('a', '2')]), ++ (';a=b', [(';a', 'b')]), ++ ('a=a+b;b=b+c', [('a', 'a b;b=b c')]), ++ ] ++ for original, expected in tests: ++ result = http.limited_parse_qsl(original, keep_blank_values=True) ++ self.assertEqual(result, expected, 'Error parsing %r' % original) ++ expect_without_blanks = [v for v in expected if len(v[1])] ++ result = http.limited_parse_qsl(original, keep_blank_values=False) ++ self.assertEqual(result, expect_without_blanks, 'Error parsing %r' % original) ++ ++ def test_parse_qsl_encoding(self): ++ result = http.limited_parse_qsl('key=\u0141%E9', encoding='latin-1') ++ self.assertEqual(result, [('key', '\u0141\xE9')]) ++ ++ if sys.version_info[0] == 2: ++ result = http.limited_parse_qsl('key=\u0141%C3%A9', encoding='utf-8') ++ self.assertEqual(result, [('key', '\u0141\xc3\xa9')]) ++ result = http.limited_parse_qsl('key=\u0141%C3%A9', encoding='ascii') ++ self.assertEqual(result, [('key', '\u0141\xc3\xa9')]) ++ result = http.limited_parse_qsl('key=\u0141%E9-', encoding='ascii') ++ self.assertEqual(result, [('key', '\u0141\xe9-')]) ++ result = http.limited_parse_qsl('key=\u0141%E9-', encoding='ascii', errors='ignore') ++ self.assertEqual(result, [('key', '\u0141\xe9-')]) ++ else: ++ result = http.limited_parse_qsl('key=\u0141%C3%A9', encoding='utf-8') ++ self.assertEqual(result, [('key', '\u0141\xE9')]) ++ result = http.limited_parse_qsl('key=\u0141%C3%A9', encoding='ascii') ++ self.assertEqual(result, [('key', '\u0141\ufffd\ufffd')]) ++ result = http.limited_parse_qsl('key=\u0141%E9-', encoding='ascii') ++ self.assertEqual(result, [('key', '\u0141\ufffd-')]) ++ result = http.limited_parse_qsl('key=\u0141%E9-', encoding='ascii', errors='ignore') ++ self.assertEqual(result, [('key', '\u0141-')]) ++ ++ def test_parse_qsl_field_limit(self): ++ with self.assertRaises(TooManyFieldsSent): ++ http.limited_parse_qsl('&'.join(['a=a'] * 11), fields_limit=10) ++ http.limited_parse_qsl('&'.join(['a=a'] * 10), fields_limit=10) diff --git a/debian/patches/series b/debian/patches/series index 296032c78..5e4af2ec9 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -6,3 +6,4 @@ 0007-Fixed-29182-Adjusted-SQLite-schema-table-alteration-.patch 0008-CVE-2020-13254.patch 0009-CVE-2020-13596.patch +0010-CVE-2021-23336.patch