diff --git a/CVE-2024-6923-follow-up-EOL-email-headers.patch b/CVE-2024-6923-follow-up-EOL-email-headers.patch index 0491c18..471369e 100644 --- a/CVE-2024-6923-follow-up-EOL-email-headers.patch +++ b/CVE-2024-6923-follow-up-EOL-email-headers.patch @@ -10,81 +10,18 @@ Co-authored-by: Denis Ledoux <5822488+beledouxdenis@users.noreply.github.com> Co-authored-by: Petr Viktorin <302922+encukou@users.noreply.github.com> Co-authored-by: Bas Bloemsaat <1586868+basbloemsaat@users.noreply.github.com> --- - Lib/email/generator.py | 12 +++++++++- - Lib/test/test_email/test_generator.py | 4 ++- - Lib/test/test_email/test_policy.py | 6 ++++- - Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst | 4 +++ - 4 files changed, 23 insertions(+), 3 deletions(-) + Lib/test/test_email/test_policy.py | 4 ++++ + Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst | 4 ++++ + 2 files changed, 8 insertions(+) create mode 100644 Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst -Index: Python-3.14.2/Lib/email/generator.py +Index: Python-3.14.3/Lib/test/test_email/test_policy.py =================================================================== ---- Python-3.14.2.orig/Lib/email/generator.py 2026-01-28 22:15:51.075267925 +0100 -+++ Python-3.14.2/Lib/email/generator.py 2026-01-28 22:15:56.251194626 +0100 -@@ -22,6 +22,7 @@ - NLCRE = re.compile(r'\r\n|\r|\n') - fcre = re.compile(r'^From ', re.MULTILINE) - NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') -+NEWLINE_WITHOUT_FWSP_BYTES = re.compile(br'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]') - - - class Generator: -@@ -429,7 +430,16 @@ - # This is almost the same as the string version, except for handling - # strings with 8bit bytes. - for h, v in msg.raw_items(): -- self._fp.write(self.policy.fold_binary(h, v)) -+ folded = self.policy.fold_binary(h, v) -+ if self.policy.verify_generated_headers: -+ linesep = self.policy.linesep.encode() -+ if not folded.endswith(linesep): -+ raise HeaderWriteError( -+ f'folded header does not end with {linesep!r}: {folded!r}') -+ if NEWLINE_WITHOUT_FWSP_BYTES.search(folded.removesuffix(linesep)): -+ raise HeaderWriteError( -+ f'folded header contains newline: {folded!r}') -+ self._fp.write(folded) - # A blank line always separates headers from body - self.write(self._NL) - -Index: Python-3.14.2/Lib/test/test_email/test_generator.py -=================================================================== ---- Python-3.14.2.orig/Lib/test/test_email/test_generator.py 2026-01-28 22:15:52.693627763 +0100 -+++ Python-3.14.2/Lib/test/test_email/test_generator.py 2026-01-28 22:15:56.251344799 +0100 -@@ -313,7 +313,7 @@ - self.assertEqual(s.getvalue(), self.typ(expected)) - - def test_verify_generated_headers(self): -- """gh-121650: by default the generator prevents header injection""" -+ # gh-121650: by default the generator prevents header injection - class LiteralHeader(str): - name = 'Header' - def fold(self, **kwargs): -@@ -334,6 +334,8 @@ - - with self.assertRaises(email.errors.HeaderWriteError): - message.as_string() -+ with self.assertRaises(email.errors.HeaderWriteError): -+ message.as_bytes() - - - class TestBytesGenerator(TestGeneratorBase, TestEmailBase): -Index: Python-3.14.2/Lib/test/test_email/test_policy.py -=================================================================== ---- Python-3.14.2.orig/Lib/test/test_email/test_policy.py 2026-01-28 22:15:52.703671956 +0100 -+++ Python-3.14.2/Lib/test/test_email/test_policy.py 2026-01-28 22:15:56.251499922 +0100 -@@ -296,7 +296,7 @@ - policy.fold("Subject", subject) - - def test_verify_generated_headers(self): -- """Turning protection off allows header injection""" -+ # Turning protection off allows header injection - policy = email.policy.default.clone(verify_generated_headers=False) - for text in ( - 'Header: Value\r\nBad: Injection\r\n', -@@ -319,6 +319,10 @@ - message.as_string(), - f"{text}\nBody", +--- Python-3.14.3.orig/Lib/test/test_email/test_policy.py 2026-02-03 16:32:20.000000000 +0100 ++++ Python-3.14.3/Lib/test/test_email/test_policy.py 2026-02-13 17:09:32.641745760 +0100 +@@ -323,6 +323,10 @@ + message.as_bytes(), + f"{text}\nBody".encode(), ) + self.assertEqual( + message.as_bytes(), @@ -93,10 +30,10 @@ Index: Python-3.14.2/Lib/test/test_email/test_policy.py # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass -Index: Python-3.14.2/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst +Index: Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.14.2/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst 2026-01-28 22:15:56.251667056 +0100 ++++ Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-21-12-34-05.gh-issue-144125.TAz5uo.rst 2026-02-13 17:09:32.642152246 +0100 @@ -0,0 +1,4 @@ +:mod:`~email.generator.BytesGenerator` will now refuse to serialize (write) headers +that are unsafely folded or delimited; see diff --git a/CVE-2025-11468-email-hdr-fold-comment.patch b/CVE-2025-11468-email-hdr-fold-comment.patch deleted file mode 100644 index e9b79e3..0000000 --- a/CVE-2025-11468-email-hdr-fold-comment.patch +++ /dev/null @@ -1,109 +0,0 @@ -From df45bd1aafc3b6792d43661207d2b7eb3a14d214 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Mon, 19 Jan 2026 06:38:22 -0600 -Subject: [PATCH] gh-143935: Email preserve parens when folding comments - (GH-143936) - -Fix a bug in the folding of comments when flattening an email message -using a modern email policy. Comments consisting of a very long sequence of -non-foldable characters could trigger a forced line wrap that omitted the -required leading space on the continuation line, causing the remainder of -the comment to be interpreted as a new header field. This enabled header -injection with carefully crafted inputs. -(cherry picked from commit 17d1490aa97bd6b98a42b1a9b324ead84e7fd8a2) - -Co-authored-by: Seth Michael Larson -Co-authored-by: Denis Ledoux ---- - Lib/email/_header_value_parser.py | 15 +++++++++++- - .../test_email/test__header_value_parser.py | 23 +++++++++++++++++++ - ...-01-16-14-40-31.gh-issue-143935.U2YtKl.rst | 6 +++++ - 3 files changed, 43 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-14-40-31.gh-issue-143935.U2YtKl.rst - -diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py -index 68c2cf9585c5b4..51727688c059ed 100644 ---- a/Lib/email/_header_value_parser.py -+++ b/Lib/email/_header_value_parser.py -@@ -101,6 +101,12 @@ def make_quoted_pairs(value): - return str(value).replace('\\', '\\\\').replace('"', '\\"') - - -+def make_parenthesis_pairs(value): -+ """Escape parenthesis and backslash for use within a comment.""" -+ return str(value).replace('\\', '\\\\') \ -+ .replace('(', '\\(').replace(')', '\\)') -+ -+ - def quote_string(value): - escaped = make_quoted_pairs(value) - return f'"{escaped}"' -@@ -939,7 +945,7 @@ def value(self): - return ' ' - - def startswith_fws(self): -- return True -+ return self and self[0] in WSP - - - class ValueTerminal(Terminal): -@@ -2959,6 +2965,13 @@ def _refold_parse_tree(parse_tree, *, policy): - [ValueTerminal(make_quoted_pairs(p), 'ptext') - for p in newparts] + - [ValueTerminal('"', 'ptext')]) -+ if part.token_type == 'comment': -+ newparts = ( -+ [ValueTerminal('(', 'ptext')] + -+ [ValueTerminal(make_parenthesis_pairs(p), 'ptext') -+ if p.token_type == 'ptext' else p -+ for p in newparts] + -+ [ValueTerminal(')', 'ptext')]) - if not part.as_ew_allowed: - wrap_as_ew_blocked += 1 - newparts.append(end_ew_not_allowed) -diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py -index 426ec4644e3096..e28fe3892015b9 100644 ---- a/Lib/test/test_email/test__header_value_parser.py -+++ b/Lib/test/test_email/test__header_value_parser.py -@@ -3294,6 +3294,29 @@ def test_address_list_with_specials_in_long_quoted_string(self): - with self.subTest(to=to): - self._test(parser.get_address_list(to)[0], folded, policy=policy) - -+ def test_address_list_with_long_unwrapable_comment(self): -+ policy = self.policy.clone(max_line_length=40) -+ cases = [ -+ # (to, folded) -+ ('(loremipsumdolorsitametconsecteturadipi)', -+ '(loremipsumdolorsitametconsecteturadipi)\n'), -+ ('(loremipsumdolorsitametconsecteturadipi)', -+ '(loremipsumdolorsitametconsecteturadipi)\n'), -+ ('(loremipsum dolorsitametconsecteturadipi)', -+ '(loremipsum dolorsitametconsecteturadipi)\n'), -+ ('(loremipsum dolorsitametconsecteturadipi)', -+ '(loremipsum\n dolorsitametconsecteturadipi)\n'), -+ ('(Escaped \\( \\) chars \\\\ in comments stay escaped)', -+ '(Escaped \\( \\) chars \\\\ in comments stay\n escaped)\n'), -+ ('((loremipsum)(loremipsum)(loremipsum)(loremipsum))', -+ '((loremipsum)(loremipsum)(loremipsum)(loremipsum))\n'), -+ ('((loremipsum)(loremipsum)(loremipsum) (loremipsum))', -+ '((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))\n'), -+ ] -+ for (to, folded) in cases: -+ with self.subTest(to=to): -+ self._test(parser.get_address_list(to)[0], folded, policy=policy) -+ - # 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. -diff --git a/Misc/NEWS.d/next/Security/2026-01-16-14-40-31.gh-issue-143935.U2YtKl.rst b/Misc/NEWS.d/next/Security/2026-01-16-14-40-31.gh-issue-143935.U2YtKl.rst -new file mode 100644 -index 00000000000000..c3d864936884ac ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2026-01-16-14-40-31.gh-issue-143935.U2YtKl.rst -@@ -0,0 +1,6 @@ -+Fixed a bug in the folding of comments when flattening an email message -+using a modern email policy. Comments consisting of a very long sequence of -+non-foldable characters could trigger a forced line wrap that omitted the -+required leading space on the continuation line, causing the remainder of -+the comment to be interpreted as a new header field. This enabled header -+injection with carefully crafted inputs. diff --git a/CVE-2025-12781-b64decode-alt-chars.patch b/CVE-2025-12781-b64decode-alt-chars.patch index 813a936..5ee2635 100644 --- a/CVE-2025-12781-b64decode-alt-chars.patch +++ b/CVE-2025-12781-b64decode-alt-chars.patch @@ -15,7 +15,7 @@ argument of b32decode(). Index: Python-3.14.3/Doc/library/base64.rst =================================================================== --- Python-3.14.3.orig/Doc/library/base64.rst 2026-02-03 16:32:20.000000000 +0100 -+++ Python-3.14.3/Doc/library/base64.rst 2026-02-05 18:27:59.082523095 +0100 ++++ Python-3.14.3/Doc/library/base64.rst 2026-02-13 15:43:18.030360439 +0100 @@ -77,15 +77,20 @@ A :exc:`binascii.Error` exception is raised if *s* is incorrectly padded. @@ -54,8 +54,8 @@ Index: Python-3.14.3/Doc/library/base64.rst Index: Python-3.14.3/Lib/base64.py =================================================================== ---- Python-3.14.3.orig/Lib/base64.py 2026-02-05 18:27:49.507586298 +0100 -+++ Python-3.14.3/Lib/base64.py 2026-02-05 18:27:59.082973222 +0100 +--- Python-3.14.3.orig/Lib/base64.py 2026-02-13 15:20:33.905228929 +0100 ++++ Python-3.14.3/Lib/base64.py 2026-02-13 15:43:18.030771327 +0100 @@ -69,20 +69,39 @@ The result is returned as a bytes object. A binascii.Error is raised if s is incorrectly padded. @@ -123,8 +123,8 @@ Index: Python-3.14.3/Lib/base64.py Index: Python-3.14.3/Lib/test/test_base64.py =================================================================== ---- Python-3.14.3.orig/Lib/test/test_base64.py 2026-02-05 18:27:50.870195970 +0100 -+++ Python-3.14.3/Lib/test/test_base64.py 2026-02-05 18:27:59.083266184 +0100 +--- Python-3.14.3.orig/Lib/test/test_base64.py 2026-02-13 15:20:35.393785541 +0100 ++++ Python-3.14.3/Lib/test/test_base64.py 2026-02-13 15:43:18.031706655 +0100 @@ -242,6 +242,25 @@ eq(base64.b64decode(data, altchars=altchars_str), res) eq(base64.b64decode(data_str, altchars=altchars_str), res) @@ -187,7 +187,7 @@ Index: Python-3.14.3/Lib/test/test_base64.py Index: Python-3.14.3/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.14.3/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst 2026-02-05 18:27:59.083646029 +0100 ++++ Python-3.14.3/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst 2026-02-13 15:43:18.032082102 +0100 @@ -0,0 +1,5 @@ +Accepting ``+`` and ``/`` characters with an alternative alphabet in +:func:`base64.b64decode` and :func:`base64.urlsafe_b64decode` is now diff --git a/CVE-2025-15366-imap-ctrl-chars.patch b/CVE-2025-15366-imap-ctrl-chars.patch index 3ca5e70..72328bf 100644 --- a/CVE-2025-15366-imap-ctrl-chars.patch +++ b/CVE-2025-15366-imap-ctrl-chars.patch @@ -11,8 +11,8 @@ Subject: [PATCH 1/2] Add 'test.support' fixture for C0 control characters Index: Python-3.14.3/Lib/imaplib.py =================================================================== ---- Python-3.14.3.orig/Lib/imaplib.py 2026-02-05 18:55:16.834011332 +0100 -+++ Python-3.14.3/Lib/imaplib.py 2026-02-05 18:55:22.293721093 +0100 +--- Python-3.14.3.orig/Lib/imaplib.py 2026-02-13 15:20:34.675850664 +0100 ++++ Python-3.14.3/Lib/imaplib.py 2026-02-13 15:43:20.726880248 +0100 @@ -131,7 +131,7 @@ # We compile these in _mode_xxx. _Literal = br'.*{(?P\d+)}$' @@ -33,8 +33,8 @@ Index: Python-3.14.3/Lib/imaplib.py literal = self.literal Index: Python-3.14.3/Lib/test/test_imaplib.py =================================================================== ---- Python-3.14.3.orig/Lib/test/test_imaplib.py 2026-02-05 18:55:18.192739303 +0100 -+++ Python-3.14.3/Lib/test/test_imaplib.py 2026-02-05 18:55:22.294159668 +0100 +--- Python-3.14.3.orig/Lib/test/test_imaplib.py 2026-02-13 15:20:36.132236378 +0100 ++++ Python-3.14.3/Lib/test/test_imaplib.py 2026-02-13 15:43:20.727593302 +0100 @@ -663,6 +663,12 @@ self.assertEqual(data[0], b'Returned to authenticated state. (Success)') self.assertEqual(client.state, 'AUTH') @@ -51,6 +51,6 @@ Index: Python-3.14.3/Lib/test/test_imaplib.py Index: Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst 2026-02-05 18:55:22.294528513 +0100 ++++ Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst 2026-02-13 15:43:20.727873249 +0100 @@ -0,0 +1 @@ +Reject control characters in IMAP commands. diff --git a/CVE-2025-15367-poplib-ctrl-chars.patch b/CVE-2025-15367-poplib-ctrl-chars.patch index bf7857b..8649e24 100644 --- a/CVE-2025-15367-poplib-ctrl-chars.patch +++ b/CVE-2025-15367-poplib-ctrl-chars.patch @@ -11,8 +11,8 @@ Subject: [PATCH 1/2] Add 'test.support' fixture for C0 control characters Index: Python-3.14.3/Lib/poplib.py =================================================================== ---- Python-3.14.3.orig/Lib/poplib.py 2026-02-05 19:22:19.999750697 +0100 -+++ Python-3.14.3/Lib/poplib.py 2026-02-05 19:22:24.426236345 +0100 +--- Python-3.14.3.orig/Lib/poplib.py 2026-02-13 15:20:34.865869684 +0100 ++++ Python-3.14.3/Lib/poplib.py 2026-02-13 15:43:22.865622881 +0100 @@ -122,6 +122,8 @@ def _putcmd(self, line): if self._debugging: print('*cmd*', repr(line)) @@ -24,8 +24,8 @@ Index: Python-3.14.3/Lib/poplib.py Index: Python-3.14.3/Lib/test/test_poplib.py =================================================================== ---- Python-3.14.3.orig/Lib/test/test_poplib.py 2026-02-05 19:22:21.690225015 +0100 -+++ Python-3.14.3/Lib/test/test_poplib.py 2026-02-05 19:22:24.426584881 +0100 +--- Python-3.14.3.orig/Lib/test/test_poplib.py 2026-02-13 15:20:36.695240465 +0100 ++++ Python-3.14.3/Lib/test/test_poplib.py 2026-02-13 15:43:22.865782353 +0100 @@ -17,6 +17,7 @@ from test.support import threading_helper from test.support import asynchat @@ -51,6 +51,6 @@ Index: Python-3.14.3/Lib/test/test_poplib.py Index: Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst 2026-02-05 19:22:24.426813762 +0100 ++++ Python-3.14.3/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst 2026-02-13 15:43:22.866393092 +0100 @@ -0,0 +1 @@ +Reject control characters in POP3 commands. diff --git a/CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch b/CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch deleted file mode 100644 index 76351cc..0000000 --- a/CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch +++ /dev/null @@ -1,209 +0,0 @@ -From 2bb0ca857e7d2593da6f6936187465a49a63c2d5 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Tue, 20 Jan 2026 15:23:42 -0600 -Subject: [PATCH] gh-143919: Reject control characters in http cookies (cherry - picked from commit 95746b3a13a985787ef53b977129041971ed7f70) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Co-authored-by: Seth Michael Larson -Co-authored-by: Bartosz Sławecki -Co-authored-by: sobolevn ---- - Doc/library/http.cookies.rst | 4 - Lib/http/cookies.py | 25 ++++ - Lib/test/support/__init__.py | 10 + - Lib/test/test_http_cookies.py | 52 +++++++++- - Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst | 1 - 5 files changed, 82 insertions(+), 10 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst - -Index: Python-3.14.2/Doc/library/http.cookies.rst -=================================================================== ---- Python-3.14.2.orig/Doc/library/http.cookies.rst 2025-12-05 17:49:16.000000000 +0100 -+++ Python-3.14.2/Doc/library/http.cookies.rst 2026-01-30 14:25:26.265077841 +0100 -@@ -292,9 +292,9 @@ - Set-Cookie: chips=ahoy - Set-Cookie: vienna=finger - >>> C = cookies.SimpleCookie() -- >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') -+ >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";') - >>> print(C) -- Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" -+ Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;" - >>> C = cookies.SimpleCookie() - >>> C["oreo"] = "doublestuff" - >>> C["oreo"]["path"] = "/" -Index: Python-3.14.2/Lib/http/cookies.py -=================================================================== ---- Python-3.14.2.orig/Lib/http/cookies.py 2026-01-30 14:25:21.316524119 +0100 -+++ Python-3.14.2/Lib/http/cookies.py 2026-01-30 14:25:26.265560727 +0100 -@@ -87,9 +87,9 @@ - such trickeries do not confuse it. - - >>> C = cookies.SimpleCookie() -- >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') -+ >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";') - >>> print(C) -- Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" -+ Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;" - - Each element of the Cookie also supports all of the RFC 2109 - Cookie attributes. Here's an example which sets the Path -@@ -170,6 +170,15 @@ - }) - - _is_legal_key = re.compile('[%s]+' % re.escape(_LegalChars)).fullmatch -+_control_character_re = re.compile(r'[\x00-\x1F\x7F]') -+ -+ -+def _has_control_character(*val): -+ """Detects control characters within a value. -+ Supports any type, as header values can be any type. -+ """ -+ return any(_control_character_re.search(str(v)) for v in val) -+ - - def _quote(str): - r"""Quote a string for use in a cookie header. -@@ -294,12 +303,16 @@ - K = K.lower() - if not K in self._reserved: - raise CookieError("Invalid attribute %r" % (K,)) -+ if _has_control_character(K, V): -+ raise CookieError(f"Control characters are not allowed in cookies {K!r} {V!r}") - dict.__setitem__(self, K, V) - - def setdefault(self, key, val=None): - key = key.lower() - if key not in self._reserved: - raise CookieError("Invalid attribute %r" % (key,)) -+ if _has_control_character(key, val): -+ raise CookieError("Control characters are not allowed in cookies %r %r" % (key, val,)) - return dict.setdefault(self, key, val) - - def __eq__(self, morsel): -@@ -335,6 +348,9 @@ - raise CookieError('Attempt to set a reserved key %r' % (key,)) - if not _is_legal_key(key): - raise CookieError('Illegal key %r' % (key,)) -+ if _has_control_character(key, val, coded_val): -+ raise CookieError( -+ "Control characters are not allowed in cookies %r %r %r" % (key, val, coded_val,)) - - # It's a good key, so save it. - self._key = key -@@ -488,7 +504,10 @@ - result = [] - items = sorted(self.items()) - for key, value in items: -- result.append(value.output(attrs, header)) -+ value_output = value.output(attrs, header) -+ if _has_control_character(value_output): -+ raise CookieError("Control characters are not allowed in cookies") -+ result.append(value_output) - return sep.join(result) - - __str__ = output -Index: Python-3.14.2/Lib/test/support/__init__.py -=================================================================== ---- Python-3.14.2.orig/Lib/test/support/__init__.py 2026-01-30 14:25:22.035209804 +0100 -+++ Python-3.14.2/Lib/test/support/__init__.py 2026-01-30 14:26:31.354376277 +0100 -@@ -68,7 +68,8 @@ - "BrokenIter", - "in_systemd_nspawn_sync_suppressed", - "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", -- "reset_code", "on_github_actions" -+ "reset_code", "on_github_actions", -+ "control_characters_c0", - ] - - -@@ -3185,3 +3186,10 @@ - return _linked_to_musl - _linked_to_musl = tuple(map(int, version.split('.'))) - return _linked_to_musl -+ -+ -+def control_characters_c0() -> list[str]: -+ """Returns a list of C0 control characters as strings. -+ C0 control characters defined as the byte range 0x00-0x1F, and 0x7F. -+ """ -+ return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"] -Index: Python-3.14.2/Lib/test/test_http_cookies.py -=================================================================== ---- Python-3.14.2.orig/Lib/test/test_http_cookies.py 2026-01-30 14:25:22.919203244 +0100 -+++ Python-3.14.2/Lib/test/test_http_cookies.py 2026-01-30 14:25:26.265943668 +0100 -@@ -17,10 +17,10 @@ - 'repr': "", - 'output': 'Set-Cookie: chips=ahoy\nSet-Cookie: vienna=finger'}, - -- {'data': 'keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"', -- 'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=\012;'}, -- 'repr': '''''', -- 'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'}, -+ {'data': 'keebler="E=mc2; L=\\"Loves\\"; fudge=;"', -+ 'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=;'}, -+ 'repr': '''''', -+ 'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=;"'}, - - # Check illegal cookies that have an '=' char in an unquoted value - {'data': 'keebler=E=mc2', -@@ -571,6 +571,50 @@ - r'Set-Cookie: key=coded_val; ' - r'expires=\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+') - -+ def test_control_characters(self): -+ for c0 in support.control_characters_c0(): -+ morsel = cookies.Morsel() -+ -+ # .__setitem__() -+ with self.assertRaises(cookies.CookieError): -+ morsel[c0] = "val" -+ with self.assertRaises(cookies.CookieError): -+ morsel["path"] = c0 -+ -+ # .setdefault() -+ with self.assertRaises(cookies.CookieError): -+ morsel.setdefault("path", c0) -+ with self.assertRaises(cookies.CookieError): -+ morsel.setdefault(c0, "val") -+ -+ # .set() -+ with self.assertRaises(cookies.CookieError): -+ morsel.set(c0, "val", "coded-value") -+ with self.assertRaises(cookies.CookieError): -+ morsel.set("path", c0, "coded-value") -+ with self.assertRaises(cookies.CookieError): -+ morsel.set("path", "val", c0) -+ -+ def test_control_characters_output(self): -+ # Tests that even if the internals of Morsel are modified -+ # that a call to .output() has control character safeguards. -+ for c0 in support.control_characters_c0(): -+ morsel = cookies.Morsel() -+ morsel.set("key", "value", "coded-value") -+ morsel._key = c0 # Override private variable. -+ cookie = cookies.SimpleCookie() -+ cookie["cookie"] = morsel -+ with self.assertRaises(cookies.CookieError): -+ cookie.output() -+ -+ morsel = cookies.Morsel() -+ morsel.set("key", "value", "coded-value") -+ morsel._coded_value = c0 # Override private variable. -+ cookie = cookies.SimpleCookie() -+ cookie["cookie"] = morsel -+ with self.assertRaises(cookies.CookieError): -+ cookie.output() -+ - - def load_tests(loader, tests, pattern): - tests.addTest(doctest.DocTestSuite(cookies)) -Index: Python-3.14.2/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.14.2/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst 2026-01-30 14:25:26.266224501 +0100 -@@ -0,0 +1 @@ -+Reject control characters in :class:`http.cookies.Morsel` fields and values. diff --git a/configure-drop-autoconf-ver-req.patch b/configure-drop-autoconf-ver-req.patch new file mode 100644 index 0000000..1a59c50 --- /dev/null +++ b/configure-drop-autoconf-ver-req.patch @@ -0,0 +1,17 @@ +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: Python-3.14.3/configure.ac +=================================================================== +--- Python-3.14.3.orig/configure.ac 2026-02-03 16:32:20.000000000 +0100 ++++ Python-3.14.3/configure.ac 2026-02-13 20:23:46.066774038 +0100 +@@ -12,7 +12,7 @@ + # Set VERSION so we only need to edit in one place (i.e., here) + m4_define([PYTHON_VERSION], [3.14]) + +-AC_PREREQ([2.72]) ++dnl AC_PREREQ([2.72]) + + AC_INIT([python],[PYTHON_VERSION],[https://github.com/python/cpython/issues/]) + diff --git a/gh139257-Support-docutils-0.22.patch b/gh139257-Support-docutils-0.22.patch index 031a094..227845d 100644 --- a/gh139257-Support-docutils-0.22.patch +++ b/gh139257-Support-docutils-0.22.patch @@ -4,13 +4,39 @@ Date: Tue, 23 Sep 2025 10:20:16 +0200 Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22 --- + Doc/Makefile | 2 - + Doc/conf.py | 3 + Doc/tools/extensions/pyspecific.py | 68 +++++++++++++++++++++++++------------ - 1 file changed, 46 insertions(+), 22 deletions(-) + 3 files changed, 50 insertions(+), 23 deletions(-) +Index: Python-3.14.3/Doc/Makefile +=================================================================== +--- Python-3.14.3.orig/Doc/Makefile 2026-02-03 16:32:20.000000000 +0100 ++++ Python-3.14.3/Doc/Makefile 2026-02-13 20:28:48.460059340 +0100 +@@ -14,7 +14,7 @@ + SOURCES = + DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) + REQUIREMENTS = requirements.txt +-SPHINXERRORHANDLING = --fail-on-warning ++SPHINXERRORHANDLING = + + # Internal variables. + PAPEROPT_a4 = --define latex_elements.papersize=a4paper +Index: Python-3.14.3/Doc/conf.py +=================================================================== +--- Python-3.14.3.orig/Doc/conf.py 2026-02-03 16:32:20.000000000 +0100 ++++ Python-3.14.3/Doc/conf.py 2026-02-13 20:21:11.034520886 +0100 +@@ -582,3 +582,6 @@ + '', + '', + ) ++ ++# Fix devhelp doc build gh#python/cpython#120150 ++master_doc = 'contents' Index: Python-3.14.3/Doc/tools/extensions/pyspecific.py =================================================================== --- Python-3.14.3.orig/Doc/tools/extensions/pyspecific.py 2026-02-03 16:32:20.000000000 +0100 -+++ Python-3.14.3/Doc/tools/extensions/pyspecific.py 2026-02-05 16:01:45.363997744 +0100 ++++ Python-3.14.3/Doc/tools/extensions/pyspecific.py 2026-02-13 17:09:31.987767795 +0100 @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- """ diff --git a/python314.changes b/python314.changes index 5a27233..61bd346 100644 --- a/python314.changes +++ b/python314.changes @@ -10,6 +10,11 @@ Thu Feb 5 17:26:23 UTC 2026 - Matej Cepl - CVE-2025-15367: basically the same as the previous patch but for the poplib library. (bsc#1257041, gh#python/cpython#143923) CVE-2025-15367-poplib-ctrl-chars.patch +- Remove upstreamed patches: + - CVE-2025-11468-email-hdr-fold-comment.patch + - CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch +- Add configure-drop-autoconf-ver-req.patch to move some `sed` + modifications to patch. ------------------------------------------------------------------- Thu Feb 5 12:57:09 UTC 2026 - Matej Cepl diff --git a/python314.spec b/python314.spec index 7ed88e9..c350716 100644 --- a/python314.spec +++ b/python314.spec @@ -203,6 +203,9 @@ Patch02: F00251-change-user-install-location.patch Patch03: python-3.3.0b1-localpath.patch # replace DATE, TIME and COMPILER by fixed definitions to aid reproducible builds Patch04: python-3.3.0b1-fix_date_time_compiler.patch +# PATCH-FIX-OPENSUSE configure-drop-autoconf-ver-req.patch mcepl@suse.com +# don't require minimal version of Autoconf +Patch05: configure-drop-autoconf-ver-req.patch # PATCH-FEATURE-UPSTREAM bpo-31046_ensurepip_honours_prefix.patch bpo#31046 mcepl@suse.com # ensurepip should honour the value of $(prefix) Patch07: bpo-31046_ensurepip_honours_prefix.patch @@ -226,12 +229,6 @@ Patch45: gh139257-Support-docutils-0.22.patch # Encode newlines in headers when using ByteGenerator # patch from gh#python/cpython#144125 Patch46: CVE-2024-6923-follow-up-EOL-email-headers.patch -# PATCH-FIX-UPSTREAM CVE-2025-11468-email-hdr-fold-comment.patch bsc#1257029 mcepl@suse.com -# Email preserve parens when folding comments -Patch47: CVE-2025-11468-email-hdr-fold-comment.patch -# PATCH-FIX-UPSTREAM CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch bsc#1257031 mcepl@suse.com -# Reject control characters in http cookies -Patch48: CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch # PATCH-FIX-UPSTREAM CVE-2025-12781-b64decode-alt-chars.patch bsc#1257108 mcepl@suse.com # Fix decoding with non-standard Base64 alphabet gh#python/cpython#125346 Patch49: CVE-2025-12781-b64decode-alt-chars.patch @@ -526,12 +523,6 @@ other applications. %prep %autosetup -p1 -n %{tarname} -# Fix devhelp doc build gh#python/cpython#120150 -echo "master_doc = 'contents'" >> Doc/conf.py - -# drop Autoconf version requirement -sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac - %if %{primary_interpreter} # fix shebangs - convert /usr/local/bin/python and /usr/bin/env/python to /usr/bin/python3 for dir in Lib Tools; do @@ -551,7 +542,7 @@ done sed -i -e '/Breakpoint 3 at ...pdb.py:97/s/97/96/' Lib/test/test_pdb.py %endif -# Cannot remove it because of gh#python/cpython#92875 +# Removing vendored expat gh#python/cpython#92875 rm -r Modules/expat # drop duplicate README from site-packages @@ -560,9 +551,6 @@ rm Lib/site-packages/README.txt # Add vendored bluez-devel files tar xvf %{SOURCE21} -# Don't fail on warnings when building documentation -sed -i -e '/^SPHINXERRORHANDLING/s/--fail-on-warning//' Doc/Makefile - %build export SUSE_VERSION="0%{?suse_version}" export SLE_VERSION="0%{?sle_version}"