File CVE-2021-23336.patch of Package python-Django1

From fd6b6afd5959b638c62dbf4839ccff97e7f7dfda Mon Sep 17 00:00:00 2001
From: Nick Pope <nick@nickpope.me.uk>
Date: Tue, 16 Feb 2021 10:14:17 +0000
Subject: [PATCH] [2.2.x] Fixed CVE-2021-23336 -- Fixed web cache poisoning via
 django.utils.http.limited_parse_qsl().

---
 django/utils/http.py                        |  2 +-
 docs/releases/2.2.19.txt                    | 16 +++++++
 docs/releases/index.txt                     |  1 +
 tests/handlers/test_exception.py            |  2 +-
 tests/requests/test_data_upload_settings.py |  8 ++--
 tests/utils_tests/test_http.py              | 51 +++++++++++++++++++--
 6 files changed, 71 insertions(+), 9 deletions(-)
 create mode 100644 docs/releases/2.2.19.txt

Index: Django-1.11.29/django/utils/http.py
===================================================================
--- Django-1.11.29.orig/django/utils/http.py
+++ Django-1.11.29/django/utils/http.py
@@ -56,7 +56,7 @@ ASCTIME_DATE = re.compile(r'^\w{3} %s %s
 RFC3986_GENDELIMS = str(":/?#[]@")
 RFC3986_SUBDELIMS = str("!$&'()*+,;=")
 
-FIELDS_MATCH = re.compile('[&;]')
+FIELDS_MATCH = re.compile('&')
 
 
 @keep_lazy_text
Index: Django-1.11.29/tests/handlers/test_exception.py
===================================================================
--- Django-1.11.29.orig/tests/handlers/test_exception.py
+++ Django-1.11.29/tests/handlers/test_exception.py
@@ -6,7 +6,7 @@ from django.test.client import FakePaylo
 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',
Index: Django-1.11.29/tests/requests/test_data_upload_settings.py
===================================================================
--- Django-1.11.29.orig/tests/requests/test_data_upload_settings.py
+++ Django-1.11.29/tests/requests/test_data_upload_settings.py
@@ -11,7 +11,7 @@ TOO_MUCH_DATA_MSG = 'Request body exceed
 
 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(Sim
                 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(Sim
             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 DataUploadMaxNumberOfFieldsMultipa
 
 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',
Index: Django-1.11.29/tests/utils_tests/test_http.py
===================================================================
--- Django-1.11.29.orig/tests/utils_tests/test_http.py
+++ Django-1.11.29/tests/utils_tests/test_http.py
@@ -7,8 +7,15 @@ from datetime import datetime
 
 from django.test import ignore_warnings
 from django.utils import http, six
+from django.core.exceptions import TooManyFieldsSent
 from django.utils.datastructures import MultiValueDict
 from django.utils.deprecation import RemovedInDjango21Warning
+from django.utils.http import (
+    base36_to_int, cookie_date, escape_leading_slashes, http_date,
+    int_to_base36, is_safe_url, is_same_domain, limited_parse_qsl, parse_etags,
+    parse_http_date, quote_etag, urlencode, urlquote, urlquote_plus,
+    urlsafe_base64_decode, urlsafe_base64_encode, urlunquote, urlunquote_plus,
+)
 
 
 class TestUtilsHttp(unittest.TestCase):
@@ -258,3 +265,47 @@ class EscapeLeadingSlashesTests(unittest
         )
         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:
+            with self.subTest(original):
+                result = 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 = 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 = limited_parse_qsl('key=\u0141%E9', encoding='latin-1')
+        self.assertEqual(result, [('key', '\u0141\xE9')])
+        result = limited_parse_qsl('key=\u0141%C3%A9', encoding='utf-8')
+        self.assertEqual(result, [('key', '\u0141\xE9')])
+        result = limited_parse_qsl('key=\u0141%C3%A9', encoding='ascii')
+        self.assertEqual(result, [('key', '\u0141\ufffd\ufffd')])
+        result = limited_parse_qsl('key=\u0141%E9-', encoding='ascii')
+        self.assertEqual(result, [('key', '\u0141\ufffd-')])
+        result = 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):
+            limited_parse_qsl('&'.join(['a=a'] * 11), fields_limit=10)
+        limited_parse_qsl('&'.join(['a=a'] * 10), fields_limit=10)
openSUSE Build Service is sponsored by