Bug#1104401: bookworm-pu: package python3.11/3.11.2-6+deb12u6
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org@packages.debian.org
Usertags: pu
X-Debbugs-Cc: security@debian.org, Matthias Klose <doko@debian.org>
* CVE-2025-0938: urlparse accepted hostname containing [ or ]
* CVE-2025-1795: Don't encode list separators in email headers
diffstat for python3.11-3.11.2 python3.11-3.11.2
changelog | 8
patches/0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch | 112 ++++++++++
patches/0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch | 60 +++++
patches/0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch | 82 +++++++
patches/series | 3
5 files changed, 265 insertions(+)
diff -Nru python3.11-3.11.2/debian/changelog python3.11-3.11.2/debian/changelog
--- python3.11-3.11.2/debian/changelog 2024-11-30 23:22:50.000000000 +0200
+++ python3.11-3.11.2/debian/changelog 2025-04-28 17:11:48.000000000 +0300
@@ -1,3 +1,11 @@
+python3.11 (3.11.2-6+deb12u6) bookworm; urgency=medium
+
+ * Non-maintainer upload.
+ * CVE-2025-0938: urlparse accepted hostname containing [ or ]
+ * CVE-2025-1795: Don't encode list separators in email headers
+
+ -- Adrian Bunk <bunk@debian.org> Mon, 28 Apr 2025 17:11:48 +0300
+
python3.11 (3.11.2-6+deb12u5) bookworm; urgency=medium
* Non-maintainer upload.
diff -Nru python3.11-3.11.2/debian/patches/0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch python3.11-3.11.2/debian/patches/0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch
--- python3.11-3.11.2/debian/patches/0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch 1970-01-01 02:00:00.000000000 +0200
+++ python3.11-3.11.2/debian/patches/0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch 2025-04-28 17:11:48.000000000 +0300
@@ -0,0 +1,112 @@
+From 4f13593827072325d77a9a880458b205ab980ad0 Mon Sep 17 00:00:00 2001
+From: "Miss Islington (bot)"
+ <31488909+miss-islington@users.noreply.github.com>
+Date: Wed, 19 Feb 2025 14:13:52 +0100
+Subject: [3.11] gh-105704: Disallow square brackets (`[` and `]`) in domain
+ names for parsed URLs (GH-129418) (#129528)
+
+Co-authored-by: Seth Michael Larson <seth@python.org>
+Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
+---
+ Lib/test/test_urlparse.py | 37 ++++++++++++++++++++++++++++++++++++-
+ Lib/urllib/parse.py | 20 ++++++++++++++++++--
+ 2 files changed, 54 insertions(+), 3 deletions(-)
+
+diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
+index 83ea618291e..45cca11da47 100644
+--- a/Lib/test/test_urlparse.py
++++ b/Lib/test/test_urlparse.py
+@@ -1103,16 +1103,51 @@ def test_invalid_bracketed_hosts(self):
+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af::2309::fae7:1234]/Path?Query')
+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@[0439:23af:2309::fae7:1234:2342:438e:192.0.2.146]/Path?Query')
+ self.assertRaises(ValueError, urllib.parse.urlsplit, 'Scheme://user@]v6a.ip[/Path')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]/')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix/')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip]?')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip].suffix?')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]/')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix/')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]?')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix?')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:a1')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:a1')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:1a')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:1a')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[::1].suffix:/')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[::1]:?')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@prefix.[v6a.ip]')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://user@[v6a.ip].suffix')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://[v6a.ip')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip]')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip[')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://]v6a.ip')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix.[v6a.ip')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip].suffix')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip[suffix')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://prefix]v6a.ip')
++ self.assertRaises(ValueError, urllib.parse.urlsplit, 'scheme://v6a.ip[suffix')
+
+ def test_splitting_bracketed_hosts(self):
+- p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]/path?query')
++ p1 = urllib.parse.urlsplit('scheme://user@[v6a.ip]:1234/path?query')
+ self.assertEqual(p1.hostname, 'v6a.ip')
+ self.assertEqual(p1.username, 'user')
+ self.assertEqual(p1.path, '/path')
++ self.assertEqual(p1.port, 1234)
+ p2 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7%test]/path?query')
+ self.assertEqual(p2.hostname, '0439:23af:2309::fae7%test')
+ self.assertEqual(p2.username, 'user')
+ self.assertEqual(p2.path, '/path')
++ self.assertIs(p2.port, None)
+ p3 = urllib.parse.urlsplit('scheme://user@[0439:23af:2309::fae7:1234:192.0.2.146%test]/path?query')
+ self.assertEqual(p3.hostname, '0439:23af:2309::fae7:1234:192.0.2.146%test')
+ self.assertEqual(p3.username, 'user')
+diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
+index e5f0b784bf6..5233020a9f0 100644
+--- a/Lib/urllib/parse.py
++++ b/Lib/urllib/parse.py
+@@ -436,6 +436,23 @@ def _checknetloc(netloc):
+ raise ValueError("netloc '" + netloc + "' contains invalid " +
+ "characters under NFKC normalization")
+
++def _check_bracketed_netloc(netloc):
++ # Note that this function must mirror the splitting
++ # done in NetlocResultMixins._hostinfo().
++ hostname_and_port = netloc.rpartition('@')[2]
++ before_bracket, have_open_br, bracketed = hostname_and_port.partition('[')
++ if have_open_br:
++ # No data is allowed before a bracket.
++ if before_bracket:
++ raise ValueError("Invalid IPv6 URL")
++ hostname, _, port = bracketed.partition(']')
++ # No data is allowed after the bracket but before the port delimiter.
++ if port and not port.startswith(":"):
++ raise ValueError("Invalid IPv6 URL")
++ else:
++ hostname, _, port = hostname_and_port.partition(':')
++ _check_bracketed_host(hostname)
++
+ # Valid bracketed hosts are defined in
+ # https://www.rfc-editor.org/rfc/rfc3986#page-49 and https://url.spec.whatwg.org/
+ def _check_bracketed_host(hostname):
+@@ -496,8 +513,7 @@ def urlsplit(url, scheme='', allow_fragments=True):
+ (']' in netloc and '[' not in netloc)):
+ raise ValueError("Invalid IPv6 URL")
+ if '[' in netloc and ']' in netloc:
+- bracketed_host = netloc.partition('[')[2].partition(']')[0]
+- _check_bracketed_host(bracketed_host)
++ _check_bracketed_netloc(netloc)
+ if allow_fragments and '#' in url:
+ url, fragment = url.split('#', 1)
+ if '?' in url:
+--
+2.30.2
+
diff -Nru python3.11-3.11.2/debian/patches/0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch python3.11-3.11.2/debian/patches/0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch
--- python3.11-3.11.2/debian/patches/0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch 1970-01-01 02:00:00.000000000 +0200
+++ python3.11-3.11.2/debian/patches/0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch 2025-04-28 17:11:21.000000000 +0300
@@ -0,0 +1,60 @@
+From b85561b1141471dcef77fc0f76d03736f9da8b92 Mon Sep 17 00:00:00 2001
+From: "Miss Islington (bot)"
+ <31488909+miss-islington@users.noreply.github.com>
+Date: Sat, 17 Feb 2024 14:01:02 +0100
+Subject: [3.11] gh-100884: email/_header_value_parser: don't encode list
+ separators (GH-100885) (GH-115593)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+ListSeparator should not be encoded. This could happen when a long line
+pushes its separator to the next line, which would have been encoded.
+(cherry picked from commit 09fab93c3d857496c0bd162797fab816c311ee48)
+
+Co-authored-by: Thomas Weißschuh <thomas@t-8ch.de>
+---
+ Lib/email/_header_value_parser.py | 3 ++-
+ Lib/test/test_email/test__header_value_parser.py | 5 +++++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
+index e1b99d5b417..87c4ae9f167 100644
+--- a/Lib/email/_header_value_parser.py
++++ b/Lib/email/_header_value_parser.py
+@@ -951,6 +951,7 @@ class _InvalidEwError(errors.HeaderParseError):
+ # up other parse trees. Maybe should have tests for that, too.
+ DOT = ValueTerminal('.', 'dot')
+ ListSeparator = ValueTerminal(',', 'list-separator')
++ListSeparator.as_ew_allowed = False
+ RouteComponentMarker = ValueTerminal('@', 'route-component-marker')
+
+ #
+@@ -2024,7 +2025,7 @@ def get_address_list(value):
+ address_list.defects.append(errors.InvalidHeaderDefect(
+ "invalid address in address-list"))
+ if value: # Must be a , at this point.
+- address_list.append(ValueTerminal(',', 'list-separator'))
++ address_list.append(ListSeparator)
+ value = value[1:]
+ return address_list, value
+
+diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py
+index 854f2ff009c..7063ce7d71c 100644
+--- a/Lib/test/test_email/test__header_value_parser.py
++++ b/Lib/test/test_email/test__header_value_parser.py
+@@ -2946,6 +2946,11 @@ def test_address_list_with_unicode_names_in_quotes(self):
+ '=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n'
+ ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n')
+
++ def test_address_list_with_list_separator_after_fold(self):
++ to = '0123456789' * 8 + '@foo, ä <foo@bar>'
++ self._test(parser.get_address_list(to)[0],
++ '0123456789' * 8 + '@foo,\n =?utf-8?q?=C3=A4?= <foo@bar>\n')
++
+ # XXX Need tests with comments on various sides of a unicode token,
+ # and with unicode tokens in the comments. Spaces inside the quotes
+ # currently don't do the right thing.
+--
+2.30.2
+
diff -Nru python3.11-3.11.2/debian/patches/0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch python3.11-3.11.2/debian/patches/0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch
--- python3.11-3.11.2/debian/patches/0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch 1970-01-01 02:00:00.000000000 +0200
+++ python3.11-3.11.2/debian/patches/0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch 2025-04-28 17:11:21.000000000 +0300
@@ -0,0 +1,82 @@
+From c9b1142da7f477f84c7d7b9324cc4883cb8a9be4 Mon Sep 17 00:00:00 2001
+From: Serhiy Storchaka <storchaka@gmail.com>
+Date: Thu, 23 May 2024 15:09:03 +0300
+Subject: [3.11] gh-118643: Fix AttributeError in the email module (GH-119099)
+ (#119393)
+
+Fix regression introduced in gh-100884: AttributeError when re-fold a long
+address list.
+
+Also fix more cases of incorrect encoding of the address separator in the
+address list missed in gh-100884.
+(cherry picked from commit 858b9e85fcdd495947c9e892ce6e3734652c48f2)
+---
+ Lib/email/_header_value_parser.py | 16 +++++++++++++---
+ Lib/test/test_email/test__header_value_parser.py | 12 ++++++++++--
+ 2 files changed, 23 insertions(+), 5 deletions(-)
+
+diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
+index 87c4ae9f167..04dbfa2ee86 100644
+--- a/Lib/email/_header_value_parser.py
++++ b/Lib/email/_header_value_parser.py
+@@ -952,6 +952,7 @@ class _InvalidEwError(errors.HeaderParseError):
+ DOT = ValueTerminal('.', 'dot')
+ ListSeparator = ValueTerminal(',', 'list-separator')
+ ListSeparator.as_ew_allowed = False
++ListSeparator.syntactic_break = False
+ RouteComponentMarker = ValueTerminal('@', 'route-component-marker')
+
+ #
+@@ -2826,13 +2827,22 @@ def _refold_parse_tree(parse_tree, *, policy):
+ if not hasattr(part, 'encode'):
+ # It's not a Terminal, do each piece individually.
+ parts = list(part) + parts
+- else:
++ want_encoding = False
++ continue
++ elif part.as_ew_allowed:
+ # It's a terminal, wrap it as an encoded word, possibly
+ # combining it with previously encoded words if allowed.
+ last_ew = _fold_as_ew(tstr, lines, maxlen, last_ew,
+ part.ew_combine_allowed, charset)
+- want_encoding = False
+- continue
++ want_encoding = False
++ continue
++ else:
++ # It's a terminal which should be kept non-encoded
++ # (e.g. a ListSeparator).
++ last_ew = None
++ want_encoding = False
++ # fall through
++
+ if len(tstr) <= maxlen - len(lines[-1]):
+ lines[-1] += tstr
+ continue
+diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py
+index 7063ce7d71c..1f575545a11 100644
+--- a/Lib/test/test_email/test__header_value_parser.py
++++ b/Lib/test/test_email/test__header_value_parser.py
+@@ -2947,9 +2947,17 @@ def test_address_list_with_unicode_names_in_quotes(self):
+ ' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n')
+
+ def test_address_list_with_list_separator_after_fold(self):
+- to = '0123456789' * 8 + '@foo, ä <foo@bar>'
++ a = 'x' * 66 + '@example.com'
++ to = f'{a}, "Hübsch Kaktus" <beautiful@example.com>'
+ self._test(parser.get_address_list(to)[0],
+- '0123456789' * 8 + '@foo,\n =?utf-8?q?=C3=A4?= <foo@bar>\n')
++ f'{a},\n =?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>\n')
++
++ a = '.' * 79
++ to = f'"{a}" <xyz@example.com>, "Hübsch Kaktus" <beautiful@example.com>'
++ self._test(parser.get_address_list(to)[0],
++ f'{a}\n'
++ ' <xyz@example.com>, =?utf-8?q?H=C3=BCbsch?= Kaktus '
++ '<beautiful@example.com>\n')
+
+ # XXX Need tests with comments on various sides of a unicode token,
+ # and with unicode tokens in the comments. Spaces inside the quotes
+--
+2.30.2
+
diff -Nru python3.11-3.11.2/debian/patches/series python3.11-3.11.2/debian/patches/series
--- python3.11-3.11.2/debian/patches/series 2024-11-30 23:22:50.000000000 +0200
+++ python3.11-3.11.2/debian/patches/series 2025-04-28 17:11:48.000000000 +0300
@@ -58,3 +58,6 @@
0003-3.11-gh-123067-Fix-quadratic-complexity-in-parsing-q.patch
0004-3.11-gh-124651-Quote-template-strings-in-venv-activa.patch
0005-3.11-gh-103848-Adds-checks-to-ensure-that-bracketed-.patch
+0001-3.11-gh-105704-Disallow-square-brackets-and-in-domai.patch
+0002-3.11-gh-100884-email-_header_value_parser-don-t-enco.patch
+0003-3.11-gh-118643-Fix-AttributeError-in-the-email-modul.patch
Reply to: