diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch index ed5dec9..f89d6be 100644 --- a/CVE-2023-52425-libexpat-2.6.0-backport.patch +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -1,15 +1,15 @@ --- - Lib/test/support/__init__.py | 16 ++++++++++++++-- + Lib/test/support/__init__.py | 22 ++++++++++++++++------ Lib/test/test_minidom.py | 23 +++++++++-------------- Lib/test/test_pyexpat.py | 12 +++++------- Lib/test/test_sax.py | 18 +++++++++--------- Lib/test/test_xml_etree.py | 12 ------------ - 5 files changed, 37 insertions(+), 44 deletions(-) + 5 files changed, 39 insertions(+), 48 deletions(-) -Index: Python-3.11.14/Lib/test/support/__init__.py +Index: Python-3.11.15/Lib/test/support/__init__.py =================================================================== ---- Python-3.11.14.orig/Lib/test/support/__init__.py 2025-11-15 19:15:08.449938538 +0100 -+++ Python-3.11.14/Lib/test/support/__init__.py 2025-11-15 19:15:12.859120260 +0100 +--- Python-3.11.15.orig/Lib/test/support/__init__.py 2026-03-09 00:22:05.833623479 +0100 ++++ Python-3.11.15/Lib/test/support/__init__.py 2026-03-09 00:25:45.751482485 +0100 @@ -8,6 +8,7 @@ import functools import os @@ -18,7 +18,7 @@ Index: Python-3.11.14/Lib/test/support/__init__.py import stat import sys import sysconfig -@@ -56,7 +57,7 @@ +@@ -56,10 +57,9 @@ "run_with_tz", "PGO", "missing_compiler_executable", "ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST", "LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT", @@ -26,39 +26,48 @@ Index: Python-3.11.14/Lib/test/support/__init__.py + "skip_on_s390x", "fails_with_expat_2_6_0", "is_expat_2_6_0" ] - -@@ -2279,6 +2280,17 @@ +- + # Timeout in seconds for tests using a network server listening on the network + # local loopback interface like 127.0.0.1. + # +@@ -2279,10 +2279,20 @@ } return ignored -#Windows doesn't have os.uname() but it doesn't support s390x. -+ +-skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', +- 'skipped on s390x') +- +# Windows doesn't have os.uname() but it doesn't support s390x. - skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', - 'skipped on s390x') -+ ++skip_on_s390x = unittest.skipIf( ++ hasattr(os, 'uname') and os.uname().machine == 's390x', ++ 'skipped on s390x') + +@functools.lru_cache +def _is_expat_2_6_0(): + return hasattr(pyexpat.ParserCreate(), 'SetReparseDeferralEnabled') ++ +is_expat_2_6_0 = _is_expat_2_6_0() + +fails_with_expat_2_6_0 = (unittest.expectedFailure + if is_expat_2_6_0 + else lambda test: test) -Index: Python-3.11.14/Lib/test/test_minidom.py + + def control_characters_c0() -> list[str]: + """Returns a list of C0 control characters as strings. +Index: Python-3.11.15/Lib/test/test_minidom.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_minidom.py 2025-11-15 19:14:53.915952608 +0100 -+++ Python-3.11.14/Lib/test/test_minidom.py 2025-11-15 19:15:12.859877278 +0100 -@@ -6,7 +6,6 @@ +--- Python-3.11.15.orig/Lib/test/test_minidom.py 2026-03-09 00:22:01.870138109 +0100 ++++ Python-3.11.15/Lib/test/test_minidom.py 2026-03-09 00:22:05.864561664 +0100 +@@ -7,7 +7,6 @@ from test import support import unittest -import pyexpat import xml.dom.minidom - from xml.dom.minidom import parse, Attr, Node, Document, parseString -@@ -1163,13 +1162,11 @@ + from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString +@@ -1194,13 +1193,11 @@ # Verify that character decoding errors raise exceptions instead # of crashing @@ -77,7 +86,7 @@ Index: Python-3.11.14/Lib/test/test_minidom.py b'Comment \xe7a va ? Tr\xe8s bien ?') doc.unlink() -@@ -1631,12 +1628,10 @@ +@@ -1662,12 +1659,10 @@ self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) def testExceptionOnSpacesInXMLNSValue(self): @@ -94,11 +103,11 @@ Index: Python-3.11.14/Lib/test/test_minidom.py parseString('') def testDocRemoveChild(self): -Index: Python-3.11.14/Lib/test/test_pyexpat.py +Index: Python-3.11.15/Lib/test/test_pyexpat.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_pyexpat.py 2025-11-15 19:14:53.915952608 +0100 -+++ Python-3.11.14/Lib/test/test_pyexpat.py 2025-11-15 19:15:12.860334045 +0100 -@@ -14,8 +14,7 @@ +--- Python-3.11.15.orig/Lib/test/test_pyexpat.py 2026-03-09 00:22:02.085337730 +0100 ++++ Python-3.11.15/Lib/test/test_pyexpat.py 2026-03-09 00:22:21.434819111 +0100 +@@ -18,8 +18,7 @@ from xml.parsers import expat from xml.parsers.expat import errors @@ -108,7 +117,7 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py class SetAttributeTest(unittest.TestCase): def setUp(self): -@@ -806,9 +805,8 @@ +@@ -810,9 +809,8 @@ self.assertIs(parser.GetReparseDeferralEnabled(), enabled) def test_reparse_deferral_enabled(self): @@ -120,7 +129,7 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py started = [] -@@ -837,9 +835,9 @@ +@@ -841,9 +839,9 @@ parser = expat.ParserCreate() parser.StartElementHandler = start_element @@ -132,10 +141,10 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py for chunk in (b''): parser.Parse(chunk, False) -Index: Python-3.11.14/Lib/test/test_sax.py +Index: Python-3.11.15/Lib/test/test_sax.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_sax.py 2025-11-15 19:14:53.915952608 +0100 -+++ Python-3.11.14/Lib/test/test_sax.py 2025-11-15 19:15:12.860746114 +0100 +--- Python-3.11.15.orig/Lib/test/test_sax.py 2026-03-09 00:22:02.128712585 +0100 ++++ Python-3.11.15/Lib/test/test_sax.py 2026-03-09 00:22:21.434819111 +0100 @@ -19,13 +19,11 @@ from io import BytesIO, StringIO import codecs @@ -187,10 +196,10 @@ Index: Python-3.11.14/Lib/test/test_sax.py self.assertFalse(parser._parser.GetReparseDeferralEnabled()) -Index: Python-3.11.14/Lib/test/test_xml_etree.py +Index: Python-3.11.15/Lib/test/test_xml_etree.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_xml_etree.py 2025-11-15 19:14:53.915952608 +0100 -+++ Python-3.11.14/Lib/test/test_xml_etree.py 2025-11-15 19:15:12.861491049 +0100 +--- Python-3.11.15.orig/Lib/test/test_xml_etree.py 2026-03-09 00:22:02.529714798 +0100 ++++ Python-3.11.15/Lib/test/test_xml_etree.py 2026-03-09 00:22:21.434819111 +0100 @@ -13,7 +13,6 @@ import operator import os diff --git a/CVE-2023-52425-remove-reparse_deferral-tests.patch b/CVE-2023-52425-remove-reparse_deferral-tests.patch index 8f183ec..28c2c7d 100644 --- a/CVE-2023-52425-remove-reparse_deferral-tests.patch +++ b/CVE-2023-52425-remove-reparse_deferral-tests.patch @@ -4,11 +4,11 @@ Lib/test/test_xml_etree.py | 2 ++ 3 files changed, 6 insertions(+) -Index: Python-3.11.14/Lib/test/test_pyexpat.py +Index: Python-3.11.15/Lib/test/test_pyexpat.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_pyexpat.py 2025-11-15 19:15:12.860334045 +0100 -+++ Python-3.11.14/Lib/test/test_pyexpat.py 2025-11-15 19:15:15.541090355 +0100 -@@ -804,6 +804,7 @@ +--- Python-3.11.15.orig/Lib/test/test_pyexpat.py 2026-03-06 16:07:07.713428031 +0100 ++++ Python-3.11.15/Lib/test/test_pyexpat.py 2026-03-06 18:23:34.307976750 +0100 +@@ -808,6 +808,7 @@ parser.SetReparseDeferralEnabled(True) self.assertIs(parser.GetReparseDeferralEnabled(), enabled) @@ -16,7 +16,7 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py def test_reparse_deferral_enabled(self): if not is_expat_2_6_0: self.skipTest("Linked libexpat doesn't support reparse deferral") -@@ -827,6 +828,7 @@ +@@ -831,6 +832,7 @@ self.assertEqual(started, ['doc']) @@ -24,10 +24,10 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py def test_reparse_deferral_disabled(self): started = [] -Index: Python-3.11.14/Lib/test/test_sax.py +Index: Python-3.11.15/Lib/test/test_sax.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_sax.py 2025-11-15 19:15:12.860746114 +0100 -+++ Python-3.11.14/Lib/test/test_sax.py 2025-11-15 19:15:15.541608234 +0100 +--- Python-3.11.15.orig/Lib/test/test_sax.py 2026-03-06 16:07:07.713855947 +0100 ++++ Python-3.11.15/Lib/test/test_sax.py 2026-03-06 18:23:34.309155833 +0100 @@ -1213,6 +1213,7 @@ self.assertEqual(result.getvalue(), start + b"text") @@ -44,10 +44,10 @@ Index: Python-3.11.14/Lib/test/test_sax.py def test_flush_reparse_deferral_disabled(self): if not is_expat_2_6_0: self.skipTest("Linked libexpat doesn't support reparse deferral") -Index: Python-3.11.14/Lib/test/test_xml_etree.py +Index: Python-3.11.15/Lib/test/test_xml_etree.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_xml_etree.py 2025-11-15 19:15:12.861491049 +0100 -+++ Python-3.11.14/Lib/test/test_xml_etree.py 2025-11-15 19:15:15.542327817 +0100 +--- Python-3.11.15.orig/Lib/test/test_xml_etree.py 2026-03-06 16:07:07.714503769 +0100 ++++ Python-3.11.15/Lib/test/test_xml_etree.py 2026-03-06 18:23:34.309977052 +0100 @@ -1620,6 +1620,7 @@ with self.assertRaises(ValueError): ET.XMLPullParser(events=('start', 'end', 'bogus')) 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 e65267d..0000000 --- a/CVE-2025-11468-email-hdr-fold-comment.patch +++ /dev/null @@ -1,109 +0,0 @@ -From dfaeb01d7859417f4e4aab8c3e6c88028c878056 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 0183a1508b1219..89950c825b6fff 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}"' -@@ -927,7 +933,7 @@ def value(self): - return ' ' - - def startswith_fws(self): -- return True -+ return self and self[0] in WSP - - - class ValueTerminal(Terminal): -@@ -2883,6 +2889,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 6025b34ac4a0f8..45ff73b5905fde 100644 ---- a/Lib/test/test_email/test__header_value_parser.py -+++ b/Lib/test/test_email/test__header_value_parser.py -@@ -3050,6 +3050,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-12084-minidom-quad-search.patch b/CVE-2025-12084-minidom-quad-search.patch deleted file mode 100644 index 91c1691..0000000 --- a/CVE-2025-12084-minidom-quad-search.patch +++ /dev/null @@ -1,93 +0,0 @@ -From b95c10349956d95e258553def0fcc52ea3ef8f82 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Wed, 3 Dec 2025 01:16:37 -0600 -Subject: [PATCH] gh-142145: Remove quadratic behavior in node ID cache - clearing (GH-142146) - -* Remove quadratic behavior in node ID cache clearing - -Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com> - -* Add news fragment - ---------- -(cherry picked from commit 08d8e18ad81cd45bc4a27d6da478b51ea49486e4) - -Co-authored-by: Seth Michael Larson -Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com> ---- - Lib/test/test_minidom.py | 18 ++++++++++ - Lib/xml/dom/minidom.py | 9 ----- - Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst | 1 - 3 files changed, 20 insertions(+), 8 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst - -Index: Python-3.11.14/Lib/test/test_minidom.py -=================================================================== ---- Python-3.11.14.orig/Lib/test/test_minidom.py 2025-12-19 22:55:59.547417036 +0100 -+++ Python-3.11.14/Lib/test/test_minidom.py 2025-12-19 22:56:07.607956864 +0100 -@@ -2,6 +2,7 @@ - - import copy - import pickle -+import time - import io - from test import support - import unittest -@@ -176,6 +177,23 @@ - self.confirm(dom.documentElement.childNodes[-1].data == "Hello") - dom.unlink() - -+ def testAppendChildNoQuadraticComplexity(self): -+ impl = getDOMImplementation() -+ -+ newdoc = impl.createDocument(None, "some_tag", None) -+ top_element = newdoc.documentElement -+ children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)] -+ element = top_element -+ -+ start = time.time() -+ for child in children: -+ element.appendChild(child) -+ element = child -+ end = time.time() -+ -+ # This example used to take at least 30 seconds. -+ self.assertLess(end - start, 1) -+ - def testAppendChildFragment(self): - dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() - dom.documentElement.appendChild(frag) -Index: Python-3.11.14/Lib/xml/dom/minidom.py -=================================================================== ---- Python-3.11.14.orig/Lib/xml/dom/minidom.py 2025-10-09 18:16:55.000000000 +0200 -+++ Python-3.11.14/Lib/xml/dom/minidom.py 2025-12-19 22:56:07.608359083 +0100 -@@ -292,13 +292,6 @@ - childNodes.append(node) - node.parentNode = self - --def _in_document(node): -- # return True iff node is part of a document tree -- while node is not None: -- if node.nodeType == Node.DOCUMENT_NODE: -- return True -- node = node.parentNode -- return False - - def _write_data(writer, data): - "Writes datachars to writer." -@@ -1539,7 +1532,7 @@ - if node.nodeType == Node.DOCUMENT_NODE: - node._id_cache.clear() - node._id_search_stack = None -- elif _in_document(node): -+ elif node.ownerDocument: - node.ownerDocument._id_cache.clear() - node.ownerDocument._id_search_stack= None - -Index: Python-3.11.14/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.11.14/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst 2025-12-19 22:56:07.608664851 +0100 -@@ -0,0 +1 @@ -+Remove quadratic behavior in ``xml.minidom`` node ID cache clearing. diff --git a/CVE-2025-12781-b64decode-alt-chars.patch b/CVE-2025-12781-b64decode-alt-chars.patch index 2028e2a..6c1cedd 100644 --- a/CVE-2025-12781-b64decode-alt-chars.patch +++ b/CVE-2025-12781-b64decode-alt-chars.patch @@ -12,10 +12,10 @@ argument of b32decode(). Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst | 5 + 4 files changed, 91 insertions(+), 14 deletions(-) -Index: Python-3.11.14/Doc/library/base64.rst +Index: Python-3.11.15/Doc/library/base64.rst =================================================================== ---- Python-3.11.14.orig/Doc/library/base64.rst 2025-10-09 18:16:55.000000000 +0200 -+++ Python-3.11.14/Doc/library/base64.rst 2026-02-11 23:44:54.612595397 +0100 +--- Python-3.11.15.orig/Doc/library/base64.rst 2026-03-03 01:52:57.000000000 +0100 ++++ Python-3.11.15/Doc/library/base64.rst 2026-03-06 19:52:36.492967768 +0100 @@ -74,15 +74,20 @@ A :exc:`binascii.Error` exception is raised if *s* is incorrectly padded. @@ -52,10 +52,10 @@ Index: Python-3.11.14/Doc/library/base64.rst .. function:: b32encode(s) -Index: Python-3.11.14/Lib/base64.py +Index: Python-3.11.15/Lib/base64.py =================================================================== ---- Python-3.11.14.orig/Lib/base64.py 2026-02-11 23:44:42.099270109 +0100 -+++ Python-3.11.14/Lib/base64.py 2026-02-11 23:44:54.613055284 +0100 +--- Python-3.11.15.orig/Lib/base64.py 2026-03-06 16:06:30.195774827 +0100 ++++ Python-3.11.15/Lib/base64.py 2026-03-06 19:52:36.493488040 +0100 @@ -71,20 +71,39 @@ The result is returned as a bytes object. A binascii.Error is raised if s is incorrectly padded. @@ -121,10 +121,10 @@ Index: Python-3.11.14/Lib/base64.py -Index: Python-3.11.14/Lib/test/test_base64.py +Index: Python-3.11.15/Lib/test/test_base64.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_base64.py 2026-02-11 23:44:44.270637438 +0100 -+++ Python-3.11.14/Lib/test/test_base64.py 2026-02-11 23:44:54.613405604 +0100 +--- Python-3.11.15.orig/Lib/test/test_base64.py 2026-03-06 16:06:32.552854037 +0100 ++++ Python-3.11.15/Lib/test/test_base64.py 2026-03-06 19:52:36.494050069 +0100 @@ -228,6 +228,25 @@ b'\xd3V\xbeo\xf7\x1d') self.check_decode_type_errors(base64.urlsafe_b64decode) @@ -181,10 +181,10 @@ Index: Python-3.11.14/Lib/test/test_base64.py def test_b32encode(self): eq = self.assertEqual -Index: Python-3.11.14/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst +Index: Python-3.11.15/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.11.14/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst 2026-02-11 23:44:54.613764682 +0100 ++++ Python-3.11.15/Misc/NEWS.d/next/Library/2025-11-06-12-03-29.gh-issue-125346.7Gfpgw.rst 2026-03-06 19:52:36.494404708 +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-13836-http-resp-cont-len.patch b/CVE-2025-13836-http-resp-cont-len.patch deleted file mode 100644 index 0fb88e7..0000000 --- a/CVE-2025-13836-http-resp-cont-len.patch +++ /dev/null @@ -1,155 +0,0 @@ -From 4f2bc24b750a82d3b439f174e7717fc09820bfeb Mon Sep 17 00:00:00 2001 -From: Serhiy Storchaka -Date: Mon, 1 Dec 2025 17:26:07 +0200 -Subject: [PATCH] gh-119451: Fix a potential denial of service in http.client - (GH-119454) - -Reading the whole body of the HTTP response could cause OOM if -the Content-Length value is too large even if the server does not send -a large amount of data. Now the HTTP client reads large data by chunks, -therefore the amount of consumed memory is proportional to the amount -of sent data. -(cherry picked from commit 5a4c4a033a4a54481be6870aa1896fad732555b5) - -Co-authored-by: Serhiy Storchaka ---- - Lib/http/client.py | 28 ++++++-- - Lib/test/test_httplib.py | 66 +++++++++++++++++++ - ...-05-23-11-47-48.gh-issue-119451.qkJe9-.rst | 5 ++ - 3 files changed, 95 insertions(+), 4 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst - -diff --git a/Lib/http/client.py b/Lib/http/client.py -index 91ee1b470cfd47..c977612732afbc 100644 ---- a/Lib/http/client.py -+++ b/Lib/http/client.py -@@ -111,6 +111,11 @@ - _MAXLINE = 65536 - _MAXHEADERS = 100 - -+# Data larger than this will be read in chunks, to prevent extreme -+# overallocation. -+_MIN_READ_BUF_SIZE = 1 << 20 -+ -+ - # Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2) - # - # VCHAR = %x21-7E -@@ -635,10 +640,25 @@ def _safe_read(self, amt): - reading. If the bytes are truly not available (due to EOF), then the - IncompleteRead exception can be used to detect the problem. - """ -- data = self.fp.read(amt) -- if len(data) < amt: -- raise IncompleteRead(data, amt-len(data)) -- return data -+ cursize = min(amt, _MIN_READ_BUF_SIZE) -+ data = self.fp.read(cursize) -+ if len(data) >= amt: -+ return data -+ if len(data) < cursize: -+ raise IncompleteRead(data, amt - len(data)) -+ -+ data = io.BytesIO(data) -+ data.seek(0, 2) -+ while True: -+ # This is a geometric increase in read size (never more than -+ # doubling out the current length of data per loop iteration). -+ delta = min(cursize, amt - cursize) -+ data.write(self.fp.read(delta)) -+ if data.tell() >= amt: -+ return data.getvalue() -+ cursize += delta -+ if data.tell() < cursize: -+ raise IncompleteRead(data.getvalue(), amt - data.tell()) - - def _safe_readinto(self, b): - """Same as _safe_read, but for reading into a buffer.""" -diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py -index 8b9d49ec094813..55363413b3b140 100644 ---- a/Lib/test/test_httplib.py -+++ b/Lib/test/test_httplib.py -@@ -1390,6 +1390,72 @@ def run_server(): - thread.join() - self.assertEqual(result, b"proxied data\n") - -+ def test_large_content_length(self): -+ serv = socket.create_server((HOST, 0)) -+ self.addCleanup(serv.close) -+ -+ def run_server(): -+ [conn, address] = serv.accept() -+ with conn: -+ while conn.recv(1024): -+ conn.sendall( -+ b"HTTP/1.1 200 Ok\r\n" -+ b"Content-Length: %d\r\n" -+ b"\r\n" % size) -+ conn.sendall(b'A' * (size//3)) -+ conn.sendall(b'B' * (size - size//3)) -+ -+ thread = threading.Thread(target=run_server) -+ thread.start() -+ self.addCleanup(thread.join, 1.0) -+ -+ conn = client.HTTPConnection(*serv.getsockname()) -+ try: -+ for w in range(15, 27): -+ size = 1 << w -+ conn.request("GET", "/") -+ with conn.getresponse() as response: -+ self.assertEqual(len(response.read()), size) -+ finally: -+ conn.close() -+ thread.join(1.0) -+ -+ def test_large_content_length_truncated(self): -+ serv = socket.create_server((HOST, 0)) -+ self.addCleanup(serv.close) -+ -+ def run_server(): -+ while True: -+ [conn, address] = serv.accept() -+ with conn: -+ conn.recv(1024) -+ if not size: -+ break -+ conn.sendall( -+ b"HTTP/1.1 200 Ok\r\n" -+ b"Content-Length: %d\r\n" -+ b"\r\n" -+ b"Text" % size) -+ -+ thread = threading.Thread(target=run_server) -+ thread.start() -+ self.addCleanup(thread.join, 1.0) -+ -+ conn = client.HTTPConnection(*serv.getsockname()) -+ try: -+ for w in range(18, 65): -+ size = 1 << w -+ conn.request("GET", "/") -+ with conn.getresponse() as response: -+ self.assertRaises(client.IncompleteRead, response.read) -+ conn.close() -+ finally: -+ conn.close() -+ size = 0 -+ conn.request("GET", "/") -+ conn.close() -+ thread.join(1.0) -+ - def test_putrequest_override_domain_validation(self): - """ - It should be possible to override the default validation -diff --git a/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst -new file mode 100644 -index 00000000000000..6d6f25cd2f8bf7 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2024-05-23-11-47-48.gh-issue-119451.qkJe9-.rst -@@ -0,0 +1,5 @@ -+Fix a potential memory denial of service in the :mod:`http.client` module. -+When connecting to a malicious server, it could cause -+an arbitrary amount of memory to be allocated. -+This could have led to symptoms including a :exc:`MemoryError`, swapping, out -+of memory (OOM) killed processes or containers, or even system crashes. diff --git a/CVE-2025-13837-plistlib-mailicious-length.patch b/CVE-2025-13837-plistlib-mailicious-length.patch deleted file mode 100644 index cef5154..0000000 --- a/CVE-2025-13837-plistlib-mailicious-length.patch +++ /dev/null @@ -1,160 +0,0 @@ -From aa9edbb11a2bf7805fd5046cdd5c2d3864aa39f2 Mon Sep 17 00:00:00 2001 -From: Serhiy Storchaka -Date: Mon, 1 Dec 2025 17:28:15 +0200 -Subject: [PATCH] [3.11] gh-119342: Fix a potential denial of service in - plistlib (GH-119343) - -Reading a specially prepared small Plist file could cause OOM because file's -read(n) preallocates a bytes object for reading the specified amount of -data. Now plistlib reads large data by chunks, therefore the upper limit of -consumed memory is proportional to the size of the input file. -(cherry picked from commit 694922cf40aa3a28f898b5f5ee08b71b4922df70) - -Co-authored-by: Serhiy Storchaka ---- - Lib/plistlib.py | 31 ++++++++++------ - Lib/test/test_plistlib.py | 37 +++++++++++++++++-- - ...-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst | 5 +++ - 3 files changed, 59 insertions(+), 14 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst - -diff --git a/Lib/plistlib.py b/Lib/plistlib.py -index 53e718f063b3ec..63fefbd5f6d499 100644 ---- a/Lib/plistlib.py -+++ b/Lib/plistlib.py -@@ -73,6 +73,9 @@ - PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__) - globals().update(PlistFormat.__members__) - -+# Data larger than this will be read in chunks, to prevent extreme -+# overallocation. -+_MIN_READ_BUF_SIZE = 1 << 20 - - class UID: - def __init__(self, data): -@@ -499,12 +502,24 @@ def _get_size(self, tokenL): - - return tokenL - -+ def _read(self, size): -+ cursize = min(size, _MIN_READ_BUF_SIZE) -+ data = self._fp.read(cursize) -+ while True: -+ if len(data) != cursize: -+ raise InvalidFileException -+ if cursize == size: -+ return data -+ delta = min(cursize, size - cursize) -+ data += self._fp.read(delta) -+ cursize += delta -+ - def _read_ints(self, n, size): -- data = self._fp.read(size * n) -+ data = self._read(size * n) - if size in _BINARY_FORMAT: - return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data) - else: -- if not size or len(data) != size * n: -+ if not size: - raise InvalidFileException() - return tuple(int.from_bytes(data[i: i + size], 'big') - for i in range(0, size * n, size)) -@@ -561,22 +576,16 @@ def _read_object(self, ref): - - elif tokenH == 0x40: # data - s = self._get_size(tokenL) -- result = self._fp.read(s) -- if len(result) != s: -- raise InvalidFileException() -+ result = self._read(s) - - elif tokenH == 0x50: # ascii string - s = self._get_size(tokenL) -- data = self._fp.read(s) -- if len(data) != s: -- raise InvalidFileException() -+ data = self._read(s) - result = data.decode('ascii') - - elif tokenH == 0x60: # unicode string - s = self._get_size(tokenL) * 2 -- data = self._fp.read(s) -- if len(data) != s: -- raise InvalidFileException() -+ data = self._read(s) - result = data.decode('utf-16be') - - elif tokenH == 0x80: # UID -diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py -index 95b7a649774dca..2bc64afdbe932f 100644 ---- a/Lib/test/test_plistlib.py -+++ b/Lib/test/test_plistlib.py -@@ -841,8 +841,7 @@ def test_xml_plist_with_entity_decl(self): - - class TestBinaryPlistlib(unittest.TestCase): - -- @staticmethod -- def decode(*objects, offset_size=1, ref_size=1): -+ def build(self, *objects, offset_size=1, ref_size=1): - data = [b'bplist00'] - offset = 8 - offsets = [] -@@ -854,7 +853,11 @@ def decode(*objects, offset_size=1, ref_size=1): - len(objects), 0, offset) - data.extend(offsets) - data.append(tail) -- return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY) -+ return b''.join(data) -+ -+ def decode(self, *objects, offset_size=1, ref_size=1): -+ data = self.build(*objects, offset_size=offset_size, ref_size=ref_size) -+ return plistlib.loads(data, fmt=plistlib.FMT_BINARY) - - def test_nonstandard_refs_size(self): - # Issue #21538: Refs and offsets are 24-bit integers -@@ -963,6 +966,34 @@ def test_invalid_binary(self): - with self.assertRaises(plistlib.InvalidFileException): - plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) - -+ def test_truncated_large_data(self): -+ self.addCleanup(os_helper.unlink, os_helper.TESTFN) -+ def check(data): -+ with open(os_helper.TESTFN, 'wb') as f: -+ f.write(data) -+ # buffered file -+ with open(os_helper.TESTFN, 'rb') as f: -+ with self.assertRaises(plistlib.InvalidFileException): -+ plistlib.load(f, fmt=plistlib.FMT_BINARY) -+ # unbuffered file -+ with open(os_helper.TESTFN, 'rb', buffering=0) as f: -+ with self.assertRaises(plistlib.InvalidFileException): -+ plistlib.load(f, fmt=plistlib.FMT_BINARY) -+ for w in range(20, 64): -+ s = 1 << w -+ # data -+ check(self.build(b'\x4f\x13' + s.to_bytes(8, 'big'))) -+ # ascii string -+ check(self.build(b'\x5f\x13' + s.to_bytes(8, 'big'))) -+ # unicode string -+ check(self.build(b'\x6f\x13' + s.to_bytes(8, 'big'))) -+ # array -+ check(self.build(b'\xaf\x13' + s.to_bytes(8, 'big'))) -+ # dict -+ check(self.build(b'\xdf\x13' + s.to_bytes(8, 'big'))) -+ # number of objects -+ check(b'bplist00' + struct.pack('>6xBBQQQ', 1, 1, s, 0, 8)) -+ - - class TestKeyedArchive(unittest.TestCase): - def test_keyed_archive_data(self): -diff --git a/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst b/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst -new file mode 100644 -index 00000000000000..04fd8faca4cf7e ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2024-05-21-22-11-31.gh-issue-119342.BTFj4Z.rst -@@ -0,0 +1,5 @@ -+Fix a potential memory denial of service in the :mod:`plistlib` module. -+When reading a Plist file received from untrusted source, it could cause -+an arbitrary amount of memory to be allocated. -+This could have led to symptoms including a :exc:`MemoryError`, swapping, out -+of memory (OOM) killed processes or containers, or even system crashes. diff --git a/CVE-2025-15282-urllib-ctrl-chars.patch b/CVE-2025-15282-urllib-ctrl-chars.patch deleted file mode 100644 index f72ad46..0000000 --- a/CVE-2025-15282-urllib-ctrl-chars.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 66c966a2d07cfcf555117309ef6aa088001bc487 Mon Sep 17 00:00:00 2001 -From: Seth Michael Larson -Date: Tue, 20 Jan 2026 14:45:58 -0600 -Subject: [PATCH] [3.11] gh-143925: Reject control characters in data: URL - mediatypes (cherry picked from commit - f25509e78e8be6ea73c811ac2b8c928c28841b9f) (cherry picked from commit - 2c9c746077d8119b5bcf5142316992e464594946) - -Co-authored-by: Seth Michael Larson ---- - Lib/test/test_urllib.py | 8 ++++++++ - Lib/urllib/request.py | 5 +++++ - .../2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst | 1 + - 3 files changed, 14 insertions(+) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst - -diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py -index f067560ca6caa1..497372a38e392a 100644 ---- a/Lib/test/test_urllib.py -+++ b/Lib/test/test_urllib.py -@@ -12,6 +12,7 @@ - from test.support import os_helper - from test.support import socket_helper - from test.support import warnings_helper -+from test.support import control_characters_c0 - import os - try: - import ssl -@@ -683,6 +684,13 @@ def test_invalid_base64_data(self): - # missing padding character - self.assertRaises(ValueError,urllib.request.urlopen,'data:;base64,Cg=') - -+ def test_invalid_mediatype(self): -+ for c0 in control_characters_c0(): -+ self.assertRaises(ValueError,urllib.request.urlopen, -+ f'data:text/html;{c0},data') -+ for c0 in control_characters_c0(): -+ self.assertRaises(ValueError,urllib.request.urlopen, -+ f'data:text/html{c0};base64,ZGF0YQ==') - - class urlretrieve_FileTests(unittest.TestCase): - """Test urllib.urlretrieve() on local files""" -diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py -index d98ba5dd1983b9..3abb7ae1b049b7 100644 ---- a/Lib/urllib/request.py -+++ b/Lib/urllib/request.py -@@ -1654,6 +1654,11 @@ def data_open(self, req): - scheme, data = url.split(":",1) - mediatype, data = data.split(",",1) - -+ # Disallow control characters within mediatype. -+ if re.search(r"[\x00-\x1F\x7F]", mediatype): -+ raise ValueError( -+ "Control characters not allowed in data: mediatype") -+ - # even base64 encoded data URLs might be quoted so unquote in any case: - data = unquote_to_bytes(data) - if mediatype.endswith(";base64"): -diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst -new file mode 100644 -index 00000000000000..46109dfbef3ee7 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-51-19.gh-issue-143925.mrtcHW.rst -@@ -0,0 +1 @@ -+Reject control characters in ``data:`` URL media types. diff --git a/CVE-2025-15366-imap-ctrl-chars.patch b/CVE-2025-15366-imap-ctrl-chars.patch index e42abab..7a5a643 100644 --- a/CVE-2025-15366-imap-ctrl-chars.patch +++ b/CVE-2025-15366-imap-ctrl-chars.patch @@ -8,10 +8,10 @@ Subject: [PATCH 1/2] Add 'test.support' fixture for C0 control characters Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) -Index: Python-3.12.12/Lib/imaplib.py +Index: Python-3.11.15/Lib/imaplib.py =================================================================== ---- Python-3.12.12.orig/Lib/imaplib.py 2026-02-10 22:15:03.417592955 +0100 -+++ Python-3.12.12/Lib/imaplib.py 2026-02-10 22:18:02.094605035 +0100 +--- Python-3.11.15.orig/Lib/imaplib.py 2026-03-06 16:06:31.545110864 +0100 ++++ Python-3.11.15/Lib/imaplib.py 2026-03-06 19:51:51.838695961 +0100 @@ -132,7 +132,7 @@ # We compile these in _mode_xxx. _Literal = br'.*{(?P\d+)}$' @@ -30,9 +30,9 @@ Index: Python-3.12.12/Lib/imaplib.py data = data + b' ' + arg literal = self.literal -Index: Python-3.12.12/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst +Index: Python-3.11.15/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.12.12/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst 2026-02-10 22:18:02.095167966 +0100 ++++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-01-16-11-41-06.gh-issue-143921.AeCOor.rst 2026-03-06 19:51:51.839096617 +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 a6308c4..57a42e8 100644 --- a/CVE-2025-15367-poplib-ctrl-chars.patch +++ b/CVE-2025-15367-poplib-ctrl-chars.patch @@ -9,10 +9,10 @@ Subject: [PATCH 1/2] Add 'test.support' fixture for C0 control characters Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst | 1 + 3 files changed, 11 insertions(+) -Index: Python-3.11.14/Lib/poplib.py +Index: Python-3.11.15/Lib/poplib.py =================================================================== ---- Python-3.11.14.orig/Lib/poplib.py 2025-10-09 18:16:55.000000000 +0200 -+++ Python-3.11.14/Lib/poplib.py 2026-02-11 23:38:35.281675745 +0100 +--- Python-3.11.15.orig/Lib/poplib.py 2026-03-06 16:06:32.025693538 +0100 ++++ Python-3.11.15/Lib/poplib.py 2026-03-06 19:52:31.051258464 +0100 @@ -122,6 +122,8 @@ def _putcmd(self, line): if self._debugging: print('*cmd*', repr(line)) @@ -22,10 +22,10 @@ Index: Python-3.11.14/Lib/poplib.py self._putline(line) -Index: Python-3.11.14/Lib/test/test_poplib.py +Index: Python-3.11.15/Lib/test/test_poplib.py =================================================================== ---- Python-3.11.14.orig/Lib/test/test_poplib.py 2025-10-09 18:16:55.000000000 +0200 -+++ Python-3.11.14/Lib/test/test_poplib.py 2026-02-11 23:39:24.009682813 +0100 +--- Python-3.11.15.orig/Lib/test/test_poplib.py 2026-03-06 16:06:33.708669394 +0100 ++++ Python-3.11.15/Lib/test/test_poplib.py 2026-03-06 19:52:31.052258474 +0100 @@ -16,6 +16,7 @@ from test.support import socket_helper from test.support import threading_helper @@ -48,9 +48,9 @@ Index: Python-3.11.14/Lib/test/test_poplib.py @requires_ssl def test_stls_capa(self): capa = self.client.capa() -Index: Python-3.11.14/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst +Index: Python-3.11.15/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.11.14/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst 2026-02-11 23:38:35.282276228 +0100 ++++ Python-3.11.15/Misc/NEWS.d/next/Security/2026-01-16-11-43-47.gh-issue-143923.DuytMe.rst 2026-03-06 19:52:31.053950556 +0100 @@ -0,0 +1 @@ +Reject control characters in POP3 commands. diff --git a/CVE-2025-6075-expandvars-perf-degrad.patch b/CVE-2025-6075-expandvars-perf-degrad.patch deleted file mode 100644 index 27ff9b8..0000000 --- a/CVE-2025-6075-expandvars-perf-degrad.patch +++ /dev/null @@ -1,359 +0,0 @@ -From e717839989908ecea9c1c8f3bd17ec9fb1ac8963 Mon Sep 17 00:00:00 2001 -From: Serhiy Storchaka -Date: Fri, 31 Oct 2025 15:49:51 +0200 -Subject: [PATCH] [3.11] gh-136065: Fix quadratic complexity in - os.path.expandvars() (GH-134952) (cherry picked from commit - f029e8db626ddc6e3a3beea4eff511a71aaceb5c) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Co-authored-by: Serhiy Storchaka -Co-authored-by: Łukasz Langa ---- - Lib/ntpath.py | 126 +++------- - Lib/posixpath.py | 43 +-- - Lib/test/test_genericpath.py | 14 + - Lib/test/test_ntpath.py | 22 + - Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst | 1 - 5 files changed, 94 insertions(+), 112 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst - -Index: Python-3.11.14/Lib/ntpath.py -=================================================================== ---- Python-3.11.14.orig/Lib/ntpath.py 2025-11-15 19:14:27.424009612 +0100 -+++ Python-3.11.14/Lib/ntpath.py 2025-11-15 19:14:41.389069009 +0100 -@@ -378,17 +378,23 @@ - # XXX With COMMAND.COM you can use any characters in a variable name, - # XXX except '^|<>='. - -+_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)" -+_varsub = None -+_varsubb = None -+ - def expandvars(path): - """Expand shell variables of the forms $var, ${var} and %var%. - - Unknown variables are left unchanged.""" - path = os.fspath(path) -+ global _varsub, _varsubb - if isinstance(path, bytes): - if b'$' not in path and b'%' not in path: - return path -- import string -- varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii') -- quote = b'\'' -+ if not _varsubb: -+ import re -+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub -+ sub = _varsubb - percent = b'%' - brace = b'{' - rbrace = b'}' -@@ -397,94 +403,44 @@ - else: - if '$' not in path and '%' not in path: - return path -- import string -- varchars = string.ascii_letters + string.digits + '_-' -- quote = '\'' -+ if not _varsub: -+ import re -+ _varsub = re.compile(_varpattern, re.ASCII).sub -+ sub = _varsub - percent = '%' - brace = '{' - rbrace = '}' - dollar = '$' - environ = os.environ -- res = path[:0] -- index = 0 -- pathlen = len(path) -- while index < pathlen: -- c = path[index:index+1] -- if c == quote: # no expansion within single quotes -- path = path[index + 1:] -- pathlen = len(path) -- try: -- index = path.index(c) -- res += c + path[:index + 1] -- except ValueError: -- res += c + path -- index = pathlen - 1 -- elif c == percent: # variable or '%' -- if path[index + 1:index + 2] == percent: -- res += c -- index += 1 -- else: -- path = path[index+1:] -- pathlen = len(path) -- try: -- index = path.index(percent) -- except ValueError: -- res += percent + path -- index = pathlen - 1 -- else: -- var = path[:index] -- try: -- if environ is None: -- value = os.fsencode(os.environ[os.fsdecode(var)]) -- else: -- value = environ[var] -- except KeyError: -- value = percent + var + percent -- res += value -- elif c == dollar: # variable or '$$' -- if path[index + 1:index + 2] == dollar: -- res += c -- index += 1 -- elif path[index + 1:index + 2] == brace: -- path = path[index+2:] -- pathlen = len(path) -- try: -- index = path.index(rbrace) -- except ValueError: -- res += dollar + brace + path -- index = pathlen - 1 -- else: -- var = path[:index] -- try: -- if environ is None: -- value = os.fsencode(os.environ[os.fsdecode(var)]) -- else: -- value = environ[var] -- except KeyError: -- value = dollar + brace + var + rbrace -- res += value -- else: -- var = path[:0] -- index += 1 -- c = path[index:index + 1] -- while c and c in varchars: -- var += c -- index += 1 -- c = path[index:index + 1] -- try: -- if environ is None: -- value = os.fsencode(os.environ[os.fsdecode(var)]) -- else: -- value = environ[var] -- except KeyError: -- value = dollar + var -- res += value -- if c: -- index -= 1 -+ -+ def repl(m): -+ lastindex = m.lastindex -+ if lastindex is None: -+ return m[0] -+ name = m[lastindex] -+ if lastindex == 1: -+ if name == percent: -+ return name -+ if not name.endswith(percent): -+ return m[0] -+ name = name[:-1] - else: -- res += c -- index += 1 -- return res -+ if name == dollar: -+ return name -+ if name.startswith(brace): -+ if not name.endswith(rbrace): -+ return m[0] -+ name = name[1:-1] -+ -+ try: -+ if environ is None: -+ return os.fsencode(os.environ[os.fsdecode(name)]) -+ else: -+ return environ[name] -+ except KeyError: -+ return m[0] -+ -+ return sub(repl, path) - - - # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. -Index: Python-3.11.14/Lib/posixpath.py -=================================================================== ---- Python-3.11.14.orig/Lib/posixpath.py 2025-11-15 19:14:27.465471369 +0100 -+++ Python-3.11.14/Lib/posixpath.py 2025-11-15 19:14:41.389334199 +0100 -@@ -287,42 +287,41 @@ - # This expands the forms $variable and ${variable} only. - # Non-existent variables are left unchanged. - --_varprog = None --_varprogb = None -+_varpattern = r'\$(\w+|\{[^}]*\}?)' -+_varsub = None -+_varsubb = None - - def expandvars(path): - """Expand shell variables of form $var and ${var}. Unknown variables - are left unchanged.""" - path = os.fspath(path) -- global _varprog, _varprogb -+ global _varsub, _varsubb - if isinstance(path, bytes): - if b'$' not in path: - return path -- if not _varprogb: -+ if not _varsubb: - import re -- _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII) -- search = _varprogb.search -+ _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub -+ sub = _varsubb - start = b'{' - end = b'}' - environ = getattr(os, 'environb', None) - else: - if '$' not in path: - return path -- if not _varprog: -+ if not _varsub: - import re -- _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) -- search = _varprog.search -+ _varsub = re.compile(_varpattern, re.ASCII).sub -+ sub = _varsub - start = '{' - end = '}' - environ = os.environ -- i = 0 -- while True: -- m = search(path, i) -- if not m: -- break -- i, j = m.span(0) -- name = m.group(1) -- if name.startswith(start) and name.endswith(end): -+ -+ def repl(m): -+ name = m[1] -+ if name.startswith(start): -+ if not name.endswith(end): -+ return m[0] - name = name[1:-1] - try: - if environ is None: -@@ -330,13 +329,11 @@ - else: - value = environ[name] - except KeyError: -- i = j -+ return m[0] - else: -- tail = path[j:] -- path = path[:i] + value -- i = len(path) -- path += tail -- return path -+ return value -+ -+ return sub(repl, path) - - - # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. -Index: Python-3.11.14/Lib/test/test_genericpath.py -=================================================================== ---- Python-3.11.14.orig/Lib/test/test_genericpath.py 2025-11-15 19:14:28.470950071 +0100 -+++ Python-3.11.14/Lib/test/test_genericpath.py 2025-11-15 19:14:41.389533528 +0100 -@@ -7,6 +7,7 @@ - import sys - import unittest - import warnings -+from test import support - from test.support import is_emscripten - from test.support import os_helper - from test.support import warnings_helper -@@ -434,6 +435,19 @@ - os.fsencode('$bar%s bar' % nonascii)) - check(b'$spam}bar', os.fsencode('%s}bar' % nonascii)) - -+ @support.requires_resource('cpu') -+ def test_expandvars_large(self): -+ expandvars = self.pathmodule.expandvars -+ with os_helper.EnvironmentVarGuard() as env: -+ env.clear() -+ env["A"] = "B" -+ n = 100_000 -+ self.assertEqual(expandvars('$A'*n), 'B'*n) -+ self.assertEqual(expandvars('${A}'*n), 'B'*n) -+ self.assertEqual(expandvars('$A!'*n), 'B!'*n) -+ self.assertEqual(expandvars('${A}A'*n), 'BA'*n) -+ self.assertEqual(expandvars('${'*10*n), '${'*10*n) -+ - def test_abspath(self): - self.assertIn("foo", self.pathmodule.abspath("foo")) - with warnings.catch_warnings(): -Index: Python-3.11.14/Lib/test/test_ntpath.py -=================================================================== ---- Python-3.11.14.orig/Lib/test/test_ntpath.py 2025-11-15 19:14:29.042372971 +0100 -+++ Python-3.11.14/Lib/test/test_ntpath.py 2025-11-15 19:14:41.389799697 +0100 -@@ -6,8 +6,8 @@ - import unittest - import warnings - from ntpath import ALLOW_MISSING --from test.support import os_helper --from test.support import TestFailed, is_emscripten -+from test import support -+from test.support import os_helper, is_emscripten - from test.support.os_helper import FakePath - from test import test_genericpath - from tempfile import TemporaryFile -@@ -57,7 +57,7 @@ - fn = fn.replace("\\", "\\\\") - gotResult = eval(fn) - if wantResult != gotResult and _norm(wantResult) != _norm(gotResult): -- raise TestFailed("%s should return: %s but returned: %s" \ -+ raise support.TestFailed("%s should return: %s but returned: %s" \ - %(str(fn), str(wantResult), str(gotResult))) - - # then with bytes -@@ -73,7 +73,7 @@ - warnings.simplefilter("ignore", DeprecationWarning) - gotResult = eval(fn) - if _norm(wantResult) != _norm(gotResult): -- raise TestFailed("%s should return: %s but returned: %s" \ -+ raise support.TestFailed("%s should return: %s but returned: %s" \ - %(str(fn), str(wantResult), repr(gotResult))) - - -@@ -820,6 +820,19 @@ - check('%spam%bar', '%sbar' % nonascii) - check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii) - -+ @support.requires_resource('cpu') -+ def test_expandvars_large(self): -+ expandvars = ntpath.expandvars -+ with os_helper.EnvironmentVarGuard() as env: -+ env.clear() -+ env["A"] = "B" -+ n = 100_000 -+ self.assertEqual(expandvars('%A%'*n), 'B'*n) -+ self.assertEqual(expandvars('%A%A'*n), 'BA'*n) -+ self.assertEqual(expandvars("''"*n + '%%'), "''"*n + '%') -+ self.assertEqual(expandvars("%%"*n), "%"*n) -+ self.assertEqual(expandvars("$$"*n), "$"*n) -+ - def test_expanduser(self): - tester('ntpath.expanduser("test")', 'test') - -@@ -1090,6 +1103,7 @@ - self.assertIsInstance(b_final_path, bytes) - self.assertGreater(len(b_final_path), 0) - -+ - class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): - pathmodule = ntpath - attributes = ['relpath'] -Index: Python-3.11.14/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.11.14/Misc/NEWS.d/next/Security/2025-05-30-22-33-27.gh-issue-136065.bu337o.rst 2025-11-15 19:14:41.390091148 +0100 -@@ -0,0 +1 @@ -+Fix quadratic complexity in :func:`os.path.expandvars`. 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 697a101..0000000 --- a/CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch +++ /dev/null @@ -1,184 +0,0 @@ -From c2d345e3e4dc8932e85dace6599e5c69a144c748 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/test_http_cookies.py | 52 +++++++++++++++++-- - ...-01-16-11-13-15.gh-issue-143919.kchwZV.rst | 1 + - 4 files changed, 73 insertions(+), 9 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst - -diff --git a/Doc/library/http.cookies.rst b/Doc/library/http.cookies.rst -index e91972fe621a48..e2abb31149ff10 100644 ---- a/Doc/library/http.cookies.rst -+++ b/Doc/library/http.cookies.rst -@@ -272,9 +272,9 @@ The following example demonstrates how to use the :mod:`http.cookies` module. - 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"] = "/" -diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py -index 2c1f021d0abede..5cfa7a8072c7f7 100644 ---- a/Lib/http/cookies.py -+++ b/Lib/http/cookies.py -@@ -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 @@ class CookieError(Exception): - }) - - _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. -@@ -292,12 +301,16 @@ def __setitem__(self, K, V): - 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): -@@ -333,6 +346,9 @@ def set(self, key, val, coded_val): - 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 -@@ -484,7 +500,10 @@ def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): - 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 -diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py -index 8879902a6e2f41..2438c57ef40458 100644 ---- a/Lib/test/test_http_cookies.py -+++ b/Lib/test/test_http_cookies.py -@@ -17,10 +17,10 @@ def test_basic(self): - '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', -@@ -517,6 +517,50 @@ def test_repr(self): - 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)) -diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst -new file mode 100644 -index 00000000000000..788c3e4ac2ebf7 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst -@@ -0,0 +1 @@ -+Reject control characters in :class:`http.cookies.Morsel` fields and values. diff --git a/CVE-2026-0865-wsgiref-ctrl-chars.patch b/CVE-2026-0865-wsgiref-ctrl-chars.patch deleted file mode 100644 index 65d2677..0000000 --- a/CVE-2026-0865-wsgiref-ctrl-chars.patch +++ /dev/null @@ -1,178 +0,0 @@ -From 2c84d7875f35d3d1d0fbc170271227cc95752fa5 Mon Sep 17 00:00:00 2001 -From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> -Date: Sat, 17 Jan 2026 10:23:57 -0800 -Subject: [PATCH] [3.11] gh-143916: Reject control characters in - wsgiref.headers.Headers (GH-143917) (GH-143973) - -gh-143916: Reject control characters in wsgiref.headers.Headers (GH-143917) - -* Add 'test.support' fixture for C0 control characters -* gh-143916: Reject control characters in wsgiref.headers.Headers - -(cherry picked from commit f7fceed79ca1bceae8dbe5ba5bc8928564da7211) -(cherry picked from commit 22e4d55285cee52bc4dbe061324e5f30bd4dee58) - -Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> -Co-authored-by: Seth Michael Larson ---- - Lib/test/support/__init__.py | 7 ++ - Lib/test/test_wsgiref.py | 18 +++++ - Lib/wsgiref/headers.py | 34 ++++++---- - Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst | 2 - 4 files changed, 47 insertions(+), 14 deletions(-) - create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst - -Index: Python-3.11.14/Lib/test/support/__init__.py -=================================================================== ---- Python-3.11.14.orig/Lib/test/support/__init__.py 2026-02-11 23:22:45.373477280 +0100 -+++ Python-3.11.14/Lib/test/support/__init__.py 2026-02-11 23:23:25.641652256 +0100 -@@ -2294,3 +2294,10 @@ - fails_with_expat_2_6_0 = (unittest.expectedFailure - if is_expat_2_6_0 - else lambda test: test) -+ -+ -+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.11.14/Lib/test/test_wsgiref.py -=================================================================== ---- Python-3.11.14.orig/Lib/test/test_wsgiref.py 2026-02-11 23:22:38.512011986 +0100 -+++ Python-3.11.14/Lib/test/test_wsgiref.py 2026-02-11 23:24:19.545119499 +0100 -@@ -1,6 +1,6 @@ - from unittest import mock - from test import support --from test.support import socket_helper -+from test.support import socket_helper, control_characters_c0 - from test.test_httpservers import NoLogRequestHandler - from unittest import TestCase - from wsgiref.util import setup_testing_defaults -@@ -503,6 +503,22 @@ - '\r\n' - ) - -+ def testRaisesControlCharacters(self): -+ for c0 in control_characters_c0(): -+ with self.subTest(c0): -+ headers = Headers() -+ self.assertRaises(ValueError, headers.__setitem__, f"key{c0}", "val") -+ self.assertRaises(ValueError, headers.add_header, f"key{c0}", "val", param="param") -+ # HTAB (\x09) is allowed in values, not names. -+ if c0 == "\t": -+ headers["key"] = f"val{c0}" -+ headers.add_header("key", f"val{c0}") -+ headers.setdefault(f"key", f"val{c0}") -+ else: -+ self.assertRaises(ValueError, headers.__setitem__, "key", f"val{c0}") -+ self.assertRaises(ValueError, headers.add_header, "key", f"val{c0}", param="param") -+ self.assertRaises(ValueError, headers.add_header, "key", "val", param=f"param{c0}") -+ - class ErrorHandler(BaseCGIHandler): - """Simple handler subclass for testing BaseHandler""" - -Index: Python-3.11.14/Lib/wsgiref/headers.py -=================================================================== ---- Python-3.11.14.orig/Lib/wsgiref/headers.py 2026-02-11 23:22:38.927685306 +0100 -+++ Python-3.11.14/Lib/wsgiref/headers.py 2026-02-11 23:24:19.545709612 +0100 -@@ -9,6 +9,11 @@ - # existence of which force quoting of the parameter value. - import re - tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') -+# Disallowed characters for headers and values. -+# HTAB (\x09) is allowed in header values, but -+# not in header names. (RFC 9110 Section 5.5) -+_name_disallowed_re = re.compile(r'[\x00-\x1F\x7F]') -+_value_disallowed_re = re.compile(r'[\x00-\x08\x0A-\x1F\x7F]') - - def _formatparam(param, value=None, quote=1): - """Convenience function to format and return a key=value pair. -@@ -35,12 +40,15 @@ - self._headers = headers - if __debug__: - for k, v in headers: -- self._convert_string_type(k) -- self._convert_string_type(v) -+ self._convert_string_type(k, name=True) -+ self._convert_string_type(v, name=False) - -- def _convert_string_type(self, value): -+ def _convert_string_type(self, value, *, name): - """Convert/check value type.""" - if type(value) is str: -+ regex = (_name_disallowed_re if name else _value_disallowed_re) -+ if regex.search(value): -+ raise ValueError("Control characters not allowed in headers") - return value - raise AssertionError("Header names/values must be" - " of type str (got {0})".format(repr(value))) -@@ -53,14 +61,14 @@ - """Set the value of a header.""" - del self[name] - self._headers.append( -- (self._convert_string_type(name), self._convert_string_type(val))) -+ (self._convert_string_type(name, name=True), self._convert_string_type(val, name=False))) - - def __delitem__(self,name): - """Delete all occurrences of a header, if present. - - Does *not* raise an exception if the header is missing. - """ -- name = self._convert_string_type(name.lower()) -+ name = self._convert_string_type(name.lower(), name=True) - self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name] - - def __getitem__(self,name): -@@ -87,13 +95,13 @@ - fields deleted and re-inserted are always appended to the header list. - If no fields exist with the given name, returns an empty list. - """ -- name = self._convert_string_type(name.lower()) -+ name = self._convert_string_type(name.lower(), name=True) - return [kv[1] for kv in self._headers if kv[0].lower()==name] - - - def get(self,name,default=None): - """Get the first header value for 'name', or return 'default'""" -- name = self._convert_string_type(name.lower()) -+ name = self._convert_string_type(name.lower(), name=True) - for k,v in self._headers: - if k.lower()==name: - return v -@@ -148,8 +156,8 @@ - and value 'value'.""" - result = self.get(name) - if result is None: -- self._headers.append((self._convert_string_type(name), -- self._convert_string_type(value))) -+ self._headers.append((self._convert_string_type(name, name=True), -+ self._convert_string_type(value, name=False))) - return value - else: - return result -@@ -172,13 +180,13 @@ - """ - parts = [] - if _value is not None: -- _value = self._convert_string_type(_value) -+ _value = self._convert_string_type(_value, name=False) - parts.append(_value) - for k, v in _params.items(): -- k = self._convert_string_type(k) -+ k = self._convert_string_type(k, name=True) - if v is None: - parts.append(k.replace('_', '-')) - else: -- v = self._convert_string_type(v) -+ v = self._convert_string_type(v, name=False) - parts.append(_formatparam(k.replace('_', '-'), v)) -- self._headers.append((self._convert_string_type(_name), "; ".join(parts))) -+ self._headers.append((self._convert_string_type(_name, name=True), "; ".join(parts))) -Index: Python-3.11.14/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ Python-3.11.14/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst 2026-02-11 23:22:49.891193395 +0100 -@@ -0,0 +1,2 @@ -+Reject C0 control characters within wsgiref.headers.Headers fields, values, -+and parameters. diff --git a/Python-3.11.14.tar.xz b/Python-3.11.14.tar.xz deleted file mode 100644 index d4542fc..0000000 --- a/Python-3.11.14.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8d3ed8ec5c88c1c95f5e558612a725450d2452813ddad5e58fdb1a53b1209b78 -size 20326860 diff --git a/Python-3.11.14.tar.xz.sigstore b/Python-3.11.14.tar.xz.sigstore deleted file mode 100644 index 6749507..0000000 --- a/Python-3.11.14.tar.xz.sigstore +++ /dev/null @@ -1 +0,0 @@ -{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzjCCAlSgAwIBAgIUa/Cqr03VT8ZG9vr8vnKbBg+smbAwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUxMDA5MTcwNjE1WhcNMjUxMDA5MTcxNjE1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV8hmCidc4iLoE7KY0EoSx+WzOVw+SRGe3TqZASn60wUXvIAGEngDUM2GJGZ2zciFho4j5aL3QiN9iXRluy1KgqOCAXMwggFvMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUTkropBHt0kanFqilhuJTKPsup4EwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGZyfAIbAAABAMASDBGAiEA4/DdAlVJKgAy0hm3sqtUk8eDDPrlFeYNhPJLNI6CgnECIQCxS/bIAbLLdlzAcy6oNNNiUOdVCU7a1Wf0qL8inGhBzTAKBggqhkjOPQQDAwNoADBlAjEAqwMoQEolvvwMVety1mnyufr6P+YikV6nd1aZZnGQAlbvwgv/mac7l0DysGh40Gs6AjAZVV5V/fNk4RpyKJCRvWkYe9ciAu6QXHbj4dWXTsjxqkx42zgZ2SOEx4951KqOX/0="}, "tlogEntries": [{"logIndex": "597736248", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1760029575", "inclusionPromise": {"signedEntryTimestamp": "MEQCIGcnPF/TJIG89Te/QJ2plfcmZaFwWMS5gwUj/i4UD+lHAiA6U8MIzjmRCPkoV9GAiUhcS8Y+EfKoxjkhkuaRsZr+1A=="}, "inclusionProof": {"logIndex": "475831986", "rootHash": "2ivwpfqfDfFhP4xZsUzcUodxk2RqHL7UJtjrVnNC+aY=", "treeSize": "475831987", "hashes": ["EcNr6novO9JFYu0MhiZEkbLuFQ0Fu33e/EikpWhJfSc=", "JUI3wLXg1JVyXKG83MkLnF48cYp3AvZ5owiKHlrcNT0=", "2/6dc8665sZ7nRZxfMqTRpwkYyvJC3vhCTtENDZ/Xg0=", "N/EKp5CRZK9LCux3vDsddWopaheQu0XNoNsWQ0+QVp8=", "CY4WrDD4KG0hsBLDiG+NQtd+muyaPZ5U48/sqDhod7s=", "b8mEVvlpxhRNmn5Z2Sg5pTM4nTx+G0MIDaE/JGACftg=", "xVDKr3yUl/tQsbx7ocbC8nBJwKmRqVd7Hl4VohToH/Y=", "BQdEs/qH6M+bN2pXY4iylKeXMJIVCIts/G7pNbq0Pe4=", "qitr//U2XNNlKMCHm7tD8fHXneUmKumQFG9lYb3sxMk=", "7hzNnRc2wXwu0fNMVF1BVE2rgN+7YiNho0SMqXqadcc=", "qXhJobQjWl6SO/pue3trUW2uL4jXx24Ip7lpd4hc5bU=", "56ObhlROm9L8Q4JyN+mxEQ5pZD5QdobB1xZFIeL0lVg=", "EGaD/cNavzxGYLx1Gl0uNNWBZvyXlSHSdlIeH7m+63A=", "2Wv4GiithwNukRKV06clevnQQYCzXmSS/+/OJtXgsXQ=", "1mfy94KpcItqshH9+gwqV6jccupcaMpVsF28New8zDY=", "vS7O4ozHIQZJWBiov+mkpI27GE8zAmVCEkRcP3NDyNE="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n475831987\n2ivwpfqfDfFhP4xZsUzcUodxk2RqHL7UJtjrVnNC+aY=\n\n\u2014 rekor.sigstore.dev wNI9ajBFAiEAnD+Qljgsyi38h/qMtYdENwkJz0F/seAi4gExs1pm4bYCIFCTOElI5MtH4Lm61GXhPyTHiQ8T8TaLZnbx6D4I8G+H\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI4ZDNlZDhlYzVjODhjMWM5NWY1ZTU1ODYxMmE3MjU0NTBkMjQ1MjgxM2RkYWQ1ZTU4ZmRiMWE1M2IxMjA5Yjc4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRUwvOHkrREM3QkpPaUVuUGM5R2F6VGN3MTZYc2xNMlNEU2dYVlEyTVl2REFpQVBrbk1GcjNRMVZoenRnR3VtVjVDUHFvb2JQTzVSUVIzYU9SanVvSXgyN1E9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZha05EUVd4VFowRjNTVUpCWjBsVllTOURjWEl3TTFaVU9GcEhPWFp5T0hadVMySkNaeXR6YldKQmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFZlRTFFUVRWTlZHTjNUbXBGTVZkb1kwNU5hbFY0VFVSQk5VMVVZM2hPYWtVeFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZXT0dodFEybGtZelJwVEc5Rk4wdFpNRVZ2VTNnclYzcFBWbmNyVTFKSFpUTlVjVm9LUVZOdU5qQjNWVmgyU1VGSFJXNW5SRlZOTWtkS1Ixb3llbU5wUm1odk5HbzFZVXd6VVdsT09XbFlVbXgxZVRGTFozRlBRMEZZVFhkblowWjJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZVYTNKdkNuQkNTSFF3YTJGdVJuRnBiR2gxU2xSTFVITjFjRFJGZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVVS1pYZENOVUZJWTBFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIxcDVaa0ZKWWtGQlFRcENRVTFCVTBSQ1IwRnBSVUUwTDBSa1FXeFdTa3RuUVhrd2FHMHpjM0YwVldzNFpVUkVVSEpzUm1WWlRtaFFTa3hPU1RaRFoyNUZRMGxSUTNoVEwySkpDa0ZpVEV4a2JIcEJZM2syYjA1T1RtbFZUMlJXUTFVM1lURlhaakJ4VERocGJrZG9RbnBVUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFSVUVLY1hkTmIxRkZiMngyZG5kTlZtVjBlVEZ0Ym5sMVpuSTJVQ3RaYVd0V05tNWtNV0ZhV201SFVVRnNZblozWjNZdmJXRmpOMnd3UkhselIyZzBNRWR6TmdwQmFrRmFWbFkxVmk5bVRtczBVbkI1UzBwRFVuWlhhMWxsT1dOcFFYVTJVVmhJWW1vMFpGZFlWSE5xZUhGcmVEUXllbWRhTWxOUFJYZzBPVFV4UzNGUENsZ3ZNRDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0="}], "timestampVerificationData": {}}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "jT7Y7FyIwclfXlWGEqclRQ0kUoE92tXlj9saU7Egm3g="}, "signature": "MEQCIEL/8y+DC7BJOiEnPc9GazTcw16XslM2SDSgXVQ2MYvDAiAPknMFr3Q1VhztgGumV5CPqoobPO5RQR3aORjuoIx27Q=="}} diff --git a/Python-3.11.15.tar.xz b/Python-3.11.15.tar.xz new file mode 100644 index 0000000..0fa0c59 --- /dev/null +++ b/Python-3.11.15.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:272179ddd9a2e41a0fc8e42e33dfbdca0b3711aa5abf372d3f2d51543d09b625 +size 20332596 diff --git a/Python-3.11.15.tar.xz.sigstore b/Python-3.11.15.tar.xz.sigstore new file mode 100644 index 0000000..28dd3a9 --- /dev/null +++ b/Python-3.11.15.tar.xz.sigstore @@ -0,0 +1 @@ +{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"certificate":{"rawBytes":"MIICzTCCAlKgAwIBAgIUVyVRcqXdAMuxUSVZptJ+6eZKm0swCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwMzAzMDEwNDIyWhcNMjYwMzAzMDExNDIyWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4bCrrwAl9ojG/jCXyN/sWSVcTvPDsHgafrczoZrmhM4YBXKoJwhhI8HHiNjKdjK/gcelYfNzm3kdGwZnEWhnHaOCAXEwggFtMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU9I5aI9ujXF6sITbCk2vxbdoUoD4wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGcsTmFPQAABAMARjBEAiAfKV4932gZgbat/glNz5FraTeDrCcEq03ta3E3tF4NBQIgFYG9TJYERGOBzl1x+MOcd+zB47NNHlAoCmILfTSdiKgwCgYIKoZIzj0EAwMDaQAwZgIxAOSxxc6G21MJac3dkN55PLRH53dyjObpwTTykZFGpTpjuUo2O3ft9QEPZitkA6Nm4wIxAPCQecOoqRnP6OEP51arhHdtMVknBJMaJ26wfyRSVsnKyi3DHF30nTOuVetJ2Fam4w=="},"tlogEntries":[{"logIndex":"1013496923","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"hashedrekord","version":"0.0.1"},"integratedTime":"1772499863","inclusionPromise":{"signedEntryTimestamp":"MEYCIQD9btJM4z88Jjs+dnFQPNitVE00joL/V0oaZYH6gxGMZgIhAKXx4hnuI60kDzRtvSOt1jZnggLTyiQF0LnSiqa4qk0D"},"inclusionProof":{"logIndex":"891592661","rootHash":"JK+1Umj5tvwdPhfBBbYD5UpiZA4fGUrt4GwehHKIcNI=","treeSize":"891592665","hashes":["s5AxwJ+t2gq0HIqW+ns8u59lldXwfv+yc2A3BGSrPis=","PAtZZmfHY9yavnppNM9Gmk+WqCJp68j2DVyB1/9QZ7w=","YpXdu4vbBuO+tHi1rd5gt5zbNu1csURZQROlGGw8StI=","oHenKqg2Pax643jd6J9plud+zRvWWr4XylTQ7ZuGesc=","2Fgbz1TsPBrhhyJNzNlf97xasPVWvjTRnxCIdVO85ZY=","BAVTdfJ//YgqqbF5CC5ly+o4KGLRQAapZTb7z0bEFFU=","vkI1W3s2WTouxaKvL+oEa85gPCaXFne20armi9bsfOw=","Kr1IZBtxX03UsOZdo6rsM2EPV0/q75toGqzT+3K/ri8=","Kjof0zMo+Hb6JNPs/Yoxt0ZOVOTDkBVzPDvA14+ONiM=","ZtgvM2m+592JolIGliHZ1mgjph/xHOzG6lmyNtQuw8o=","yaLcjLp9ldnUTWyy/PouoOddMS22Et9RPJrGE7WCDYc=","50BdkX7nkRgG4UrWoLVyZogLPlcHZQP2khTmOteFbho=","UFjcw2ByTVPBmHuBXkpFOLynDtU0JPa0rs1lvSVm9ns=","UfqWnx1YuXWnR6tQB8LboYpg7AuaUmwROEn7wJBVDjM=","HU7296NTl8Wek0AyCPeTMXdik0fZbtCiBDPDqVeye0E=","Xo5tam8gxbsWohATkFEqn5hvHpPFwBJ0SDjNE5DiI2A=","ZleKYeRKwUF3HP3HO0kxHMVeJgY3N/euGinVhlVWaq0=","fLAvE46NqCVV86EpB2pKkwJlFjjFk7ntX3lC+PiZuIo=","T4DqWD42hAtN+vX8jKCWqoC4meE4JekI9LxYGCcPy1M="],"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n891592665\nJK+1Umj5tvwdPhfBBbYD5UpiZA4fGUrt4GwehHKIcNI=\n\n— rekor.sigstore.dev wNI9ajBEAiAltxt+A0tLVa+o0H/ajiGUgmqyTo4EnQ61UY0mMB8GewIgaNKdAMRpHkk9y09X1pjyH++ayq8uM37PI8q8rJNEYYg=\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyNzIxNzlkZGQ5YTJlNDFhMGZjOGU0MmUzM2RmYmRjYTBiMzcxMWFhNWFiZjM3MmQzZjJkNTE1NDNkMDliNjI1In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUURlTUZ3ZHVwZ0lKaWxJWHJ6SWtTS0dhcXV1dlRNS3BYZnJZZ25qbzNpbXl3SWhBTzVxdzRTMkI5WW9WbkpxOGhMTUdUakk0Uk1VL3hYdTlCZWxVenRXQlRiSiIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZWRU5EUVd4TFowRjNTVUpCWjBsVlZubFdVbU54V0dSQlRYVjRWVk5XV25CMFNpczJaVnBMYlRCemQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDE2UVhwTlJFVjNUa1JKZVZkb1kwNU5hbGwzVFhwQmVrMUVSWGhPUkVsNVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUwWWtOeWNuZEJiRGx2YWtjdmFrTlllVTR2YzFkVFZtTlVkbEJFYzBobllXWnlZM29LYjFweWJXaE5ORmxDV0V0dlNuZG9hRWs0U0VocFRtcExaR3BMTDJkalpXeFpaazU2YlROclpFZDNXbTVGVjJodVNHRlBRMEZZUlhkblowWjBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlU1U1RWaENrazVkV3BZUmpaelNWUmlRMnN5ZG5oaVpHOVZiMFEwZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRLUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBVS1pWRkNNMEZJVlVFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyTnpWRzFHVUZGQlFRcENRVTFCVW1wQ1JVRnBRV1pMVmpRNU16Sm5XbWRpWVhRdloyeE9lalZHY21GVVpVUnlRMk5GY1RBemRHRXpSVE4wUmpST1FsRkpaMFpaUnpsVVNsbEZDbEpIVDBKNmJERjRLMDFQWTJRcmVrSTBOMDVPU0d4QmIwTnRTVXhtVkZOa2FVdG5kME5uV1VsTGIxcEplbW93UlVGM1RVUmhVVUYzV21kSmVFRlBVM2dLZUdNMlJ6SXhUVXBoWXpOa2EwNDFOVkJNVWtnMU0yUjVhazlpY0hkVVZIbHJXa1pIY0ZSd2FuVlZiekpQTTJaME9WRkZVRnBwZEd0Qk5rNXROSGRKZUFwQlVFTlJaV05QYjNGU2JsQTJUMFZRTlRGaGNtaElaSFJOVm10dVFrcE5ZVW95Tm5kbWVWSlRWbk51UzNscE0wUklSak13YmxSUGRWWmxkRW95Um1GdENqUjNQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0="}],"timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIIE6jADAgEAMIIE4QYJKoZIhvcNAQcCoIIE0jCCBM4CAQMxDTALBglghkgBZQMEAgEwgcIGCyqGSIb3DQEJEAEEoIGyBIGvMIGsAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgBlVGEfl0/jl+NOiYqgPhx9Y6GPmDU/KL9fvcf0aNr44CFQCQB7SaN05xLwMMLTF9PcntJo4EyRgPMjAyNjAzMDMwMTA0MjNaMAMCAQECCHfOPhf2vU81oDKkMDAuMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxFTATBgNVBAMTDHNpZ3N0b3JlLXRzYaCCAhQwggIQMIIBlqADAgECAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkMB4XDTI1MDQwODA2NTk0M1oXDTM1MDQwNjA2NTk0M1owLjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MRUwEwYDVQQDEwxzaWdzdG9yZS10c2EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATitrZnyEo2KDZP2QWMIBOgYbfSOTL5ZC/cHMv6Yq+HVIo1H9TC7Cx80KDiyvKhgB3wTqKyi9UDczhqg12b1AOLnRnydMTK+qB8M+1MjBci1+Jb8AV/VXu7CRuQCiPTHFyjajBoMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUif15Q4fP0GVGwwJGxyxzW3206wMwHwYDVR0jBBgwFoAUmOwB73+7Uf/UlR5vioiYUweJzr8wFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwCgYIKoZIzj0EAwMDaAAwZQIwO2mxX/opo7SrIX9QyxfZpJRcpAV2gZOm1AZzR+2rVyy6Uc8Ybp2ybIw13ckH4bcRAjEA5qO8FyOkmYpvg2/7ZNqiPxRzn5vqKHoVcIIqtpKq6l7TvOqzAxxclN7VwTG8e++XMYIB2zCCAdcCAQEwUTA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDALBglghkgBZQMEAgGggfwwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjAzMDMwMTA0MjNaMC8GCSqGSIb3DQEJBDEiBCD5NBdRMWFs/yHd18Ieiy21PmhCfG+jo/7KrvIcHwYoWzCBjgYLKoZIhvcNAQkQAi8xfzB9MHsweQQghfknvAerYsrDtENWwQ78gbLGiD/aernm2HDZ0TrNBbcwVTA9pDswOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUOhNULwyQYe68wUMvy4qOiyojiwwwCgYIKoZIzj0EAwIEZzBlAjEAxklFvFksLU54DXUFBzPyV5IkH+rmD2qd366o3p93HRUEGuCrd7vbH1NFjLm3TVh8AjAjU0CW80Ez9El7s3R06h1VqONDo8mmkV/j2Mo1D93iHMk7Sn2uaQhe5N6EK4diBII="}]}},"messageSignature":{"messageDigest":{"algorithm":"SHA2_256","digest":"JyF53dmi5BoPyOQuM9+9ygs3EapavzctPy1RVD0JtiU="},"signature":"MEYCIQDeMFwdupgIJilIXrzIkSKGaquuvTMKpXfrYgnjo3imywIhAO5qw4S2B9YoVnJq8hLMGTjI4RMU/xXu9BelUztWBTbJ"}} diff --git a/bso1227999-reproducible-builds.patch b/bso1227999-reproducible-builds.patch index fb33d18..bf2d3ff 100644 --- a/bso1227999-reproducible-builds.patch +++ b/bso1227999-reproducible-builds.patch @@ -12,9 +12,11 @@ for the definition of this variable. Doc/library/functions.rst | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) ---- a/Doc/conf.py -+++ b/Doc/conf.py -@@ -316,7 +316,8 @@ html_context = { +Index: Python-3.11.15/Doc/conf.py +=================================================================== +--- Python-3.11.15.orig/Doc/conf.py 2026-03-03 01:52:57.000000000 +0100 ++++ Python-3.11.15/Doc/conf.py 2026-03-06 18:23:39.828089970 +0100 +@@ -316,7 +316,8 @@ } # This 'Last updated on:' timestamp is inserted at the bottom of every page. @@ -24,9 +26,11 @@ for the definition of this variable. # Path to find HTML templates. templates_path = ['tools/templates'] ---- a/Doc/library/functions.rst -+++ b/Doc/library/functions.rst -@@ -1356,7 +1356,7 @@ are always available. They are listed h +Index: Python-3.11.15/Doc/library/functions.rst +=================================================================== +--- Python-3.11.15.orig/Doc/library/functions.rst 2026-03-03 01:52:57.000000000 +0100 ++++ Python-3.11.15/Doc/library/functions.rst 2026-03-06 18:23:39.829089979 +0100 +@@ -1356,7 +1356,7 @@ (where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`, and :mod:`shutil`. diff --git a/fix_configure_rst.patch b/fix_configure_rst.patch index 2c10100..00f843d 100644 --- a/fix_configure_rst.patch +++ b/fix_configure_rst.patch @@ -3,10 +3,10 @@ Misc/NEWS | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) -Index: Python-3.11.14/Doc/using/configure.rst +Index: Python-3.11.15/Doc/using/configure.rst =================================================================== ---- Python-3.11.14.orig/Doc/using/configure.rst 2025-11-15 19:14:54.096952433 +0100 -+++ Python-3.11.14/Doc/using/configure.rst 2025-11-15 19:15:04.439920979 +0100 +--- Python-3.11.15.orig/Doc/using/configure.rst 2026-03-06 16:06:43.304945441 +0100 ++++ Python-3.11.15/Doc/using/configure.rst 2026-03-06 16:06:55.576758292 +0100 @@ -43,7 +43,6 @@ See :data:`sys.int_info.bits_per_digit `. @@ -29,11 +29,11 @@ Index: Python-3.11.14/Doc/using/configure.rst .. option:: --enable-framework=INSTALLDIR Create a Python.framework rather than a traditional Unix install. Optional -Index: Python-3.11.14/Misc/NEWS +Index: Python-3.11.15/Misc/NEWS =================================================================== ---- Python-3.11.14.orig/Misc/NEWS 2025-11-15 19:14:54.096952433 +0100 -+++ Python-3.11.14/Misc/NEWS 2025-11-15 19:15:04.445942414 +0100 -@@ -9987,7 +9987,7 @@ +--- Python-3.11.15.orig/Misc/NEWS 2026-03-06 16:06:43.304945441 +0100 ++++ Python-3.11.15/Misc/NEWS 2026-03-06 16:06:55.580059032 +0100 +@@ -10081,7 +10081,7 @@ - bpo-40939: Removed documentation for the removed ``PyParser_*`` C API. - bpo-43795: The list in :ref:`limited-api-list` now shows the public name diff --git a/python311.changes b/python311.changes index 43a11fd..d38631f 100644 --- a/python311.changes +++ b/python311.changes @@ -1,3 +1,93 @@ +------------------------------------------------------------------- +Fri Mar 6 18:54:51 UTC 2026 - Matej Cepl + +- Update to 3.11.15: + - Security + - gh-144125: BytesGenerator will now refuse to serialize + (write) headers that are unsafely folded or delimited; see + verify_generated_headers. (Contributed by Bas Bloemsaat and + Petr Viktorin in gh-121650). + - gh-143935: 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 (bsc#1257029 CVE-2025-11468). + - gh-143925: Reject control characters in data: URL media + types (bsc#1257046, CVE-2025-15282). + - gh-143919: Reject control characters in http.cookies.Morsel + fields and values (bsc#1257031, CVE-2026-0672). + - gh-143916: Reject C0 control characters within + wsgiref.headers.Headers fields, values, and parameters + (bsc#1257042, CVE-2026-0865). + - gh-142145: Remove quadratic behavior in xml.minidom node ID + cache clearing. In order to do this without breaking + existing users, we also add the ownerDocument attribute to + xml.dom.minidom elements and attributes created by directly + instantiating the Element or Attr class. Note that this way + of creating nodes is not supported; creator functions like + xml.dom.Document.documentElement() should be used instead + (bsc#1254997, CVE-2025-12084). + - gh-137836: Add support of the “plaintext” element, RAWTEXT + elements “xmp”, “iframe”, “noembed” and “noframes”, and + optionally RAWTEXT element “noscript” in + html.parser.HTMLParser. + - gh-136063: email.message: ensure linear complexity for + legacy HTTP parameters parsing. Patch by Bénédikt Tran. + - gh-136065: Fix quadratic complexity in + os.path.expandvars() (bsc#1252974, CVE-2025-6075). + - gh-119451: Fix a potential memory denial of service in the + http.client module. When connecting to a malicious server, + it could cause an arbitrary amount of memory to be + allocated. This could have led to symptoms including + a MemoryError, swapping, out of memory (OOM) killed + processes or containers, or even system crashes + (CVE-2025-13836, bsc#1254400). + - gh-119452: Fix a potential memory denial of service in the + http.server module. When a malicious user is connected to + the CGI server on Windows, it could cause an arbitrary + amount of memory to be allocated. This could have led to + symptoms including a MemoryError, swapping, out of memory + (OOM) killed processes or containers, or even system + crashes. + - gh-119342: Fix a potential memory denial of service in the + plistlib module. When reading a Plist file received from + untrusted source, it could cause an arbitrary amount of + memory to be allocated. This could have led to symptoms + including a MemoryError, swapping, out of memory (OOM) + killed processes or containers, or even system crashes + (bsc#1254401, CVE-2025-13837). + - Library + - gh-144833: Fixed a use-after-free in ssl when SSL_new() + returns NULL in newPySSLSocket(). The error was reported + via a dangling pointer after the object had already been + freed. + - gh-144363: Update bundled libexpat to 2.7.4 + - gh-90949: Add SetAllocTrackerActivationThreshold() and + SetAllocTrackerMaximumAmplification() to xmlparser objects + to prevent use of disproportional amounts of dynamic memory + from within an Expat parser. Patch by Bénédikt Tran. + - Core and Builtins + - gh-120384: Fix an array out of bounds crash in + list_ass_subscript, which could be invoked via some + specificly tailored input: including concurrent + modification of a list object, where one thread assigns + a slice and another clears it. + - gh-120298: Fix use-after free in list_richcompare_impl + which can be invoked via some specificly tailored evil + input. +Remove upstreamed patches: + - CVE-2025-11468-email-hdr-fold-comment.patch + - CVE-2025-12084-minidom-quad-search.patch + - CVE-2025-13836-http-resp-cont-len.patch + - CVE-2025-13837-plistlib-mailicious-length.patch + - CVE-2025-6075-expandvars-perf-degrad.patch + - CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch + - CVE-2026-0865-wsgiref-ctrl-chars.patch + - CVE-2025-15282-urllib-ctrl-chars.patch + ------------------------------------------------------------------- Wed Feb 11 19:09:06 CET 2026 - Matej Cepl diff --git a/python311.spec b/python311.spec index a2f5344..d694f5c 100644 --- a/python311.spec +++ b/python311.spec @@ -107,7 +107,7 @@ # _md5.cpython-38m-x86_64-linux-gnu.so %define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so Name: %{python_pkg_name}%{psuffix} -Version: 3.11.14 +Version: 3.11.15 Release: 0 Summary: Python 3 Interpreter License: Python-2.0 @@ -188,33 +188,9 @@ Patch22: gh120226-fix-sendfile-test-kernel-610.patch Patch24: add-loongarch64-support.patch # PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com Patch25: gh139257-Support-docutils-0.22.patch -# PATCH-FIX-UPSTREAM CVE-2025-6075-expandvars-perf-degrad.patch bsc#1252974 mcepl@suse.com -# Avoid potential quadratic complexity vulnerabilities in path modules -Patch26: CVE-2025-6075-expandvars-perf-degrad.patch -# PATCH-FIX-UPSTREAM CVE-2025-13836-http-resp-cont-len.patch bsc#1254400 mcepl@suse.com -# Avoid loading possibly compromised length of HTTP response -Patch27: CVE-2025-13836-http-resp-cont-len.patch -# PATCH-FIX-UPSTREAM CVE-2025-12084-minidom-quad-search.patch bsc#1254997 mcepl@suse.com -# prevent quadratic behavior in node ID cache clearing -Patch28: CVE-2025-12084-minidom-quad-search.patch -# PATCH-FIX-UPSTREAM CVE-2025-13837-plistlib-mailicious-length.patch bsc#1254401 mcepl@suse.com -# protect against OOM when loading malicious content -Patch29: CVE-2025-13837-plistlib-mailicious-length.patch -# PATCH-FIX-UPSTREAM CVE-2025-11468-email-hdr-fold-comment.patch bsc#1257029 mcepl@suse.com -# this patch makes things totally awesome -Patch30: 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 -# rejects control characters in http cookies. -Patch31: CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch -# PATCH-FIX-UPSTREAM CVE-2026-0865-wsgiref-ctrl-chars.patch bsc#1257042 mcepl@suse.com -# Reject control characters in wsgiref.headers.Headers -Patch32: CVE-2026-0865-wsgiref-ctrl-chars.patch # PATCH-FIX-UPSTREAM CVE-2025-15366-imap-ctrl-chars.patch bsc#1257044 mcepl@suse.com # Reject control characters in wsgiref.headers.Headers Patch33: CVE-2025-15366-imap-ctrl-chars.patch -# PATCH-FIX-UPSTREAM CVE-2025-15282-urllib-ctrl-chars.patch bsc#1257046 mcepl@suse.com -# Reject control characters in urllib -Patch34: CVE-2025-15282-urllib-ctrl-chars.patch # PATCH-FIX-UPSTREAM CVE-2025-15367-poplib-ctrl-chars.patch bsc#1257041 mcepl@suse.com # Reject control characters in poplib Patch35: CVE-2025-15367-poplib-ctrl-chars.patch