diff -Nru python-django-2.2.20/debian/changelog python-django-2.2.20/debian/changelog --- python-django-2.2.20/debian/changelog 2021-04-28 10:36:37.000000000 +0000 +++ python-django-2.2.20/debian/changelog 2021-05-26 12:52:14.000000000 +0000 @@ -1,3 +1,24 @@ +python-django (2:2.2.20-1ubuntu0.2) hirsute-security; urgency=medium + + * SECURITY UPDATE: header injection in URLValidator with Python 3.9.5+ + - debian/patches/CVE-2021-32052.patch: prevent newlines and tabs from + being accepted in URLValidator in django/core/validators.py, + tests/validators/tests.py. + - CVE-2021-32052 + * SECURITY UPDATE: potential directory traversal via admindocs + - debian/patches/CVE-2021-33203.patch: use safe_join in + django/contrib/admindocs/views.py, tests/admin_docs/test_views.py. + - CVE-2021-33203 + * SECURITY UPDATE: possible indeterminate SSRF, RFI, and LFI attacks + since validators accepted leading zeros in IPv4 addresses + - debian/patches/CVE-2021-33571.patch: prevent leading zeros in IPv4 + addresses in django/core/validators.py, + tests/validators/invalid_urls.txt, tests/validators/tests.py, + tests/validators/valid_urls.txt. + - CVE-2021-33571 + + -- Marc Deslauriers Wed, 26 May 2021 08:52:14 -0400 + python-django (2:2.2.20-1ubuntu0.1) hirsute-security; urgency=medium * SECURITY UPDATE: Potential directory-traversal via uploaded files diff -Nru python-django-2.2.20/debian/patches/CVE-2021-32052.patch python-django-2.2.20/debian/patches/CVE-2021-32052.patch --- python-django-2.2.20/debian/patches/CVE-2021-32052.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-django-2.2.20/debian/patches/CVE-2021-32052.patch 2021-05-26 12:51:56.000000000 +0000 @@ -0,0 +1,70 @@ +From d9594c4ea57b6309d93879805302cec9ae9f23ff Mon Sep 17 00:00:00 2001 +From: Mariusz Felisiak +Date: Tue, 4 May 2021 20:50:12 +0200 +Subject: [PATCH] [2.2.x] Fixed #32713, Fixed CVE-2021-32052 -- Prevented + newlines and tabs from being accepted in URLValidator on Python 3.9.5+. + +In Python 3.9.5+ urllib.parse() automatically removes ASCII newlines +and tabs from URLs [1, 2]. Unfortunately it created an issue in +the URLValidator. URLValidator uses urllib.urlsplit() and +urllib.urlunsplit() for creating a URL variant with Punycode which no +longer contains newlines and tabs in Python 3.9.5+. As a consequence, +the regular expression matched the URL (without unsafe characters) and +the source value (with unsafe characters) was considered valid. + +[1] https://bugs.python.org/issue43882 and +[2] https://github.com/python/cpython/commit/76cd81d60310d65d01f9d7b48a8985d8ab89c8b4 + +Backport of e1e81aa1c4427411e3c68facdd761229ffea6f6f from main. +--- + django/core/validators.py | 5 ++++- + docs/releases/2.2.22.txt | 22 ++++++++++++++++++++++ + docs/releases/index.txt | 1 + + tests/validators/tests.py | 8 +++++++- + 4 files changed, 34 insertions(+), 2 deletions(-) + create mode 100644 docs/releases/2.2.22.txt + +diff --git a/django/core/validators.py b/django/core/validators.py +index 38e4b6aa1d7a..d32b54f68c55 100644 +--- a/django/core/validators.py ++++ b/django/core/validators.py +@@ -101,6 +101,7 @@ class URLValidator(RegexValidator): + r'\Z', re.IGNORECASE) + message = _('Enter a valid URL.') + schemes = ['http', 'https', 'ftp', 'ftps'] ++ unsafe_chars = frozenset('\t\r\n') + + def __init__(self, schemes=None, **kwargs): + super().__init__(**kwargs) +@@ -108,7 +109,9 @@ def __init__(self, schemes=None, **kwargs): + self.schemes = schemes + + def __call__(self, value): +- # Check first if the scheme is valid ++ if isinstance(value, str) and self.unsafe_chars.intersection(value): ++ raise ValidationError(self.message, code=self.code) ++ # Check if the scheme is valid. + scheme = value.split('://')[0].lower() + if scheme not in self.schemes: + raise ValidationError(self.message, code=self.code) +diff --git a/tests/validators/tests.py b/tests/validators/tests.py +index 36d0b2a520b3..012b098f4e2a 100644 +--- a/tests/validators/tests.py ++++ b/tests/validators/tests.py +@@ -222,9 +222,15 @@ + (URLValidator(EXTENDED_SCHEMES), 'git+ssh://git@github.com/example/hg-git.git', None), + + (URLValidator(EXTENDED_SCHEMES), 'git://-invalid.com', ValidationError), +- # Trailing newlines not accepted ++ # Newlines and tabs are not accepted. + (URLValidator(), 'http://www.djangoproject.com/\n', ValidationError), + (URLValidator(), 'http://[::ffff:192.9.5.5]\n', ValidationError), ++ (URLValidator(), 'http://www.djangoproject.com/\r', ValidationError), ++ (URLValidator(), 'http://[::ffff:192.9.5.5]\r', ValidationError), ++ (URLValidator(), 'http://www.django\rproject.com/', ValidationError), ++ (URLValidator(), 'http://[::\rffff:192.9.5.5]', ValidationError), ++ (URLValidator(), 'http://\twww.djangoproject.com/', ValidationError), ++ (URLValidator(), 'http://\t[::ffff:192.9.5.5]', ValidationError), + # Trailing junk does not take forever to reject + (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br ', ValidationError), + (URLValidator(), 'http://www.asdasdasdasdsadfm.com.br z', ValidationError), diff -Nru python-django-2.2.20/debian/patches/CVE-2021-33203.patch python-django-2.2.20/debian/patches/CVE-2021-33203.patch --- python-django-2.2.20/debian/patches/CVE-2021-33203.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-django-2.2.20/debian/patches/CVE-2021-33203.patch 2021-05-26 12:52:01.000000000 +0000 @@ -0,0 +1,63 @@ +From e8533625cff17b41daf2352abadb74fa4209d911 Mon Sep 17 00:00:00 2001 +From: Florian Apolloner +Date: Tue, 25 May 2021 11:55:06 +0200 +Subject: [PATCH] [2.2.x] Fixed CVE-2021-33203 -- Fixed potential + path-traversal via admindocs' TemplateDetailView. + +--- + django/contrib/admindocs/views.py | 3 ++- + docs/releases/2.2.24.txt | 12 +++++++++++- + tests/admin_docs/test_views.py | 16 ++++++++++++++++ + 3 files changed, 29 insertions(+), 2 deletions(-) + +diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py +index 0474c38fd4..5986717d95 100644 +--- a/django/contrib/admindocs/views.py ++++ b/django/contrib/admindocs/views.py +@@ -15,6 +15,7 @@ from django.db import models + from django.http import Http404 + from django.template.engine import Engine + from django.urls import get_mod_func, get_resolver, get_urlconf ++from django.utils._os import safe_join + from django.utils.decorators import method_decorator + from django.utils.inspect import ( + func_accepts_kwargs, func_accepts_var_args, get_func_full_args, +@@ -328,7 +329,7 @@ class TemplateDetailView(BaseAdminDocsView): + else: + # This doesn't account for template loaders (#24128). + for index, directory in enumerate(default_engine.dirs): +- template_file = Path(directory) / template ++ template_file = Path(safe_join(directory, template)) + if template_file.exists(): + with template_file.open() as f: + template_contents = f.read() +diff --git a/tests/admin_docs/test_views.py b/tests/admin_docs/test_views.py +index bcadff7d8a..dc6d3c127b 100644 +--- a/tests/admin_docs/test_views.py ++++ b/tests/admin_docs/test_views.py +@@ -134,6 +134,22 @@ class AdminDocViewTests(TestDataMixin, AdminDocsTestCase): + self.assertContains(response, 'View documentation') + + ++@unittest.skipUnless(utils.docutils_is_available, 'no docutils installed.') ++class AdminDocViewDefaultEngineOnly(TestDataMixin, AdminDocsTestCase): ++ ++ def setUp(self): ++ self.client.force_login(self.superuser) ++ ++ def test_template_detail_path_traversal(self): ++ cases = ['/etc/passwd', '../passwd'] ++ for fpath in cases: ++ with self.subTest(path=fpath): ++ response = self.client.get( ++ reverse('django-admindocs-templates', args=[fpath]), ++ ) ++ self.assertEqual(response.status_code, 400) ++ ++ + @override_settings(TEMPLATES=[{ + 'NAME': 'ONE', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', +-- +2.31.1 + diff -Nru python-django-2.2.20/debian/patches/CVE-2021-33571.patch python-django-2.2.20/debian/patches/CVE-2021-33571.patch --- python-django-2.2.20/debian/patches/CVE-2021-33571.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-django-2.2.20/debian/patches/CVE-2021-33571.patch 2021-05-26 12:52:08.000000000 +0000 @@ -0,0 +1,128 @@ +From 048eb4f1ac4756a0ae496a77c10ee53a54a69d67 Mon Sep 17 00:00:00 2001 +From: Mariusz Felisiak +Date: Tue, 25 May 2021 11:57:59 +0200 +Subject: [PATCH] [2.2.x] Fixed CVE-2021-33571 -- Prevented leading zeros in + IPv4 addresses. + +validate_ipv4_address() was affected only on Python < 3.9.5, see [1]. +URLValidator() uses a regular expressions and it was affected on all +Python versions. + +[1] https://bugs.python.org/issue36384 +--- + django/core/validators.py | 14 +++++++++++++- + docs/releases/2.2.24.txt | 13 +++++++++++++ + tests/validators/invalid_urls.txt | 8 ++++++++ + tests/validators/tests.py | 20 ++++++++++++++++++++ + tests/validators/valid_urls.txt | 6 ++++++ + 5 files changed, 60 insertions(+), 1 deletion(-) + +diff --git a/django/core/validators.py b/django/core/validators.py +index d32b54f68c..2da0688e28 100644 +--- a/django/core/validators.py ++++ b/django/core/validators.py +@@ -75,7 +75,7 @@ class URLValidator(RegexValidator): + ul = '\u00a1-\uffff' # unicode letters range (must not be a raw string) + + # IP patterns +- ipv4_re = r'(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' ++ ipv4_re = r'(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)(?:\.(?:0|25[0-5]|2[0-4]\d|1\d?\d?|[1-9]\d?)){3}' + ipv6_re = r'\[[0-9a-f:\.]+\]' # (simple regex, validated later) + + # Host patterns +@@ -256,6 +256,18 @@ def validate_ipv4_address(value): + ipaddress.IPv4Address(value) + except ValueError: + raise ValidationError(_('Enter a valid IPv4 address.'), code='invalid') ++ else: ++ # Leading zeros are forbidden to avoid ambiguity with the octal ++ # notation. This restriction is included in Python 3.9.5+. ++ # TODO: Remove when dropping support for PY39. ++ if any( ++ octet != '0' and octet[0] == '0' ++ for octet in value.split('.') ++ ): ++ raise ValidationError( ++ _('Enter a valid IPv4 address.'), ++ code='invalid', ++ ) + + + def validate_ipv6_address(value): +diff --git a/tests/validators/invalid_urls.txt b/tests/validators/invalid_urls.txt +index 4a092034ff..a5a41ba845 100644 +--- a/tests/validators/invalid_urls.txt ++++ b/tests/validators/invalid_urls.txt +@@ -46,6 +46,14 @@ http://1.1.1.1.1 + http://123.123.123 + http://3628126748 + http://123 ++http://000.000.000.000 ++http://016.016.016.016 ++http://192.168.000.001 ++http://01.2.3.4 ++http://01.2.3.4 ++http://1.02.3.4 ++http://1.2.03.4 ++http://1.2.3.04 + http://.www.foo.bar/ + http://.www.foo.bar./ + http://[::1:2::3]:8080/ +diff --git a/tests/validators/tests.py b/tests/validators/tests.py +index 012b098f4e..1f09fb53fc 100644 +--- a/tests/validators/tests.py ++++ b/tests/validators/tests.py +@@ -135,6 +135,16 @@ TEST_DATA = [ + (validate_ipv4_address, '1.1.1.1\n', ValidationError), + (validate_ipv4_address, '٧.2٥.3٣.243', ValidationError), + ++ # Leading zeros are forbidden to avoid ambiguity with the octal notation. ++ (validate_ipv4_address, '000.000.000.000', ValidationError), ++ (validate_ipv4_address, '016.016.016.016', ValidationError), ++ (validate_ipv4_address, '192.168.000.001', ValidationError), ++ (validate_ipv4_address, '01.2.3.4', ValidationError), ++ (validate_ipv4_address, '01.2.3.4', ValidationError), ++ (validate_ipv4_address, '1.02.3.4', ValidationError), ++ (validate_ipv4_address, '1.2.03.4', ValidationError), ++ (validate_ipv4_address, '1.2.3.04', ValidationError), ++ + # validate_ipv6_address uses django.utils.ipv6, which + # is tested in much greater detail in its own testcase + (validate_ipv6_address, 'fe80::1', None), +@@ -160,6 +170,16 @@ TEST_DATA = [ + (validate_ipv46_address, '::zzz', ValidationError), + (validate_ipv46_address, '12345::', ValidationError), + ++ # Leading zeros are forbidden to avoid ambiguity with the octal notation. ++ (validate_ipv46_address, '000.000.000.000', ValidationError), ++ (validate_ipv46_address, '016.016.016.016', ValidationError), ++ (validate_ipv46_address, '192.168.000.001', ValidationError), ++ (validate_ipv46_address, '01.2.3.4', ValidationError), ++ (validate_ipv46_address, '01.2.3.4', ValidationError), ++ (validate_ipv46_address, '1.02.3.4', ValidationError), ++ (validate_ipv46_address, '1.2.03.4', ValidationError), ++ (validate_ipv46_address, '1.2.3.04', ValidationError), ++ + (validate_comma_separated_integer_list, '1', None), + (validate_comma_separated_integer_list, '12', None), + (validate_comma_separated_integer_list, '1,2', None), +diff --git a/tests/validators/valid_urls.txt b/tests/validators/valid_urls.txt +index f79f948142..ef9e563f8e 100644 +--- a/tests/validators/valid_urls.txt ++++ b/tests/validators/valid_urls.txt +@@ -63,6 +63,12 @@ http://0.0.0.0/ + http://255.255.255.255 + http://224.0.0.0 + http://224.1.1.1 ++http://111.112.113.114/ ++http://88.88.88.88/ ++http://11.12.13.14/ ++http://10.20.30.40/ ++http://1.2.3.4/ ++http://127.0.01.09.home.lan + http://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com + http://example.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com + http://example.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +-- +2.31.1 + diff -Nru python-django-2.2.20/debian/patches/series python-django-2.2.20/debian/patches/series --- python-django-2.2.20/debian/patches/series 2021-04-28 10:35:58.000000000 +0000 +++ python-django-2.2.20/debian/patches/series 2021-05-26 12:52:08.000000000 +0000 @@ -4,3 +4,6 @@ 0004-Set-the-default-shebang-to-new-projects-to-use-Pytho.patch 0005-Use-usr-bin-env-python3-shebang-for-django-admin.py.patch CVE-2021-31542.patch +CVE-2021-32052.patch +CVE-2021-33203.patch +CVE-2021-33571.patch