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