1 Commits

Author SHA256 Message Date
55aed1f43b Merge pull request 'Factory' (#1) from factory into main 2025-10-08 12:57:27 +02:00
17 changed files with 453 additions and 966 deletions

View File

@@ -6,10 +6,10 @@
Lib/test/test_xml_etree.py | 12 ------------
5 files changed, 37 insertions(+), 44 deletions(-)
Index: Python-3.11.14/Lib/test/support/__init__.py
Index: Python-3.11.12/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.12.orig/Lib/test/support/__init__.py 2025-04-11 10:52:43.191010503 +0200
+++ Python-3.11.12/Lib/test/support/__init__.py 2025-04-11 10:52:44.802161741 +0200
@@ -8,6 +8,7 @@
import functools
import os
@@ -27,7 +27,7 @@ Index: Python-3.11.14/Lib/test/support/__init__.py
]
@@ -2279,6 +2280,17 @@
@@ -2244,6 +2245,17 @@
}
return ignored
@@ -46,10 +46,10 @@ Index: Python-3.11.14/Lib/test/support/__init__.py
+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
Index: Python-3.11.12/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
--- Python-3.11.12.orig/Lib/test/test_minidom.py 2025-04-11 10:52:21.907086938 +0200
+++ Python-3.11.12/Lib/test/test_minidom.py 2025-04-11 10:52:44.802522893 +0200
@@ -6,7 +6,6 @@
from test import support
import unittest
@@ -94,10 +94,10 @@ Index: Python-3.11.14/Lib/test/test_minidom.py
parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
def testDocRemoveChild(self):
Index: Python-3.11.14/Lib/test/test_pyexpat.py
Index: Python-3.11.12/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
--- Python-3.11.12.orig/Lib/test/test_pyexpat.py 2025-04-11 10:52:22.076696906 +0200
+++ Python-3.11.12/Lib/test/test_pyexpat.py 2025-04-11 10:52:44.803228085 +0200
@@ -14,8 +14,7 @@
from xml.parsers import expat
from xml.parsers.expat import errors
@@ -108,7 +108,7 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py
class SetAttributeTest(unittest.TestCase):
def setUp(self):
@@ -806,9 +805,8 @@
@@ -770,9 +769,8 @@
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
def test_reparse_deferral_enabled(self):
@@ -120,7 +120,7 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py
started = []
@@ -837,9 +835,9 @@
@@ -801,9 +799,9 @@
parser = expat.ParserCreate()
parser.StartElementHandler = start_element
@@ -132,10 +132,10 @@ Index: Python-3.11.14/Lib/test/test_pyexpat.py
for chunk in (b'<doc', b'/>'):
parser.Parse(chunk, False)
Index: Python-3.11.14/Lib/test/test_sax.py
Index: Python-3.11.12/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.12.orig/Lib/test/test_sax.py 2025-04-11 10:52:22.111440337 +0200
+++ Python-3.11.12/Lib/test/test_sax.py 2025-04-11 10:52:44.803567098 +0200
@@ -19,13 +19,11 @@
from io import BytesIO, StringIO
import codecs
@@ -187,10 +187,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.12/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.12.orig/Lib/test/test_xml_etree.py 2025-04-11 10:52:22.425637912 +0200
+++ Python-3.11.12/Lib/test/test_xml_etree.py 2025-04-11 10:52:44.804234785 +0200
@@ -13,7 +13,6 @@
import operator
import os

View File

@@ -4,11 +4,9 @@
Lib/test/test_xml_etree.py | 2 ++
3 files changed, 6 insertions(+)
Index: Python-3.11.14/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 @@
--- a/Lib/test/test_pyexpat.py
+++ b/Lib/test/test_pyexpat.py
@@ -768,6 +768,7 @@ class ReparseDeferralTest(unittest.TestC
parser.SetReparseDeferralEnabled(True)
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
@@ -16,7 +14,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 @@
@@ -791,6 +792,7 @@ class ReparseDeferralTest(unittest.TestC
self.assertEqual(started, ['doc'])
@@ -24,11 +22,9 @@ 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
===================================================================
--- 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
@@ -1213,6 +1213,7 @@
--- a/Lib/test/test_sax.py
+++ b/Lib/test/test_sax.py
@@ -1213,6 +1213,7 @@ class ExpatReaderTest(XmlTestBase):
self.assertEqual(result.getvalue(), start + b"<doc>text</doc>")
@@ -36,7 +32,7 @@ Index: Python-3.11.14/Lib/test/test_sax.py
def test_flush_reparse_deferral_enabled(self):
if not is_expat_2_6_0:
self.skipTest("Linked libexpat doesn't support reparse deferral")
@@ -1238,6 +1239,7 @@
@@ -1238,6 +1239,7 @@ class ExpatReaderTest(XmlTestBase):
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
@@ -44,11 +40,9 @@ 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
===================================================================
--- 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
@@ -1620,6 +1620,7 @@
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1620,6 +1620,7 @@ class XMLPullParserTest(unittest.TestCas
with self.assertRaises(ValueError):
ET.XMLPullParser(events=('start', 'end', 'bogus'))
@@ -56,7 +50,7 @@ Index: Python-3.11.14/Lib/test/test_xml_etree.py
def test_flush_reparse_deferral_enabled(self):
parser = ET.XMLPullParser(events=('start', 'end'))
@@ -1641,6 +1642,7 @@
@@ -1641,6 +1642,7 @@ class XMLPullParserTest(unittest.TestCas
self.assert_event_tags(parser, [('end', 'doc')])

View File

@@ -1,93 +0,0 @@
From b95c10349956d95e258553def0fcc52ea3ef8f82 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <seth@python.org>
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 <seth@python.org>
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.

View File

@@ -1,155 +0,0 @@
From 4f2bc24b750a82d3b439f174e7717fc09820bfeb Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
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 <storchaka@gmail.com>
---
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.

View File

@@ -1,160 +0,0 @@
From aa9edbb11a2bf7805fd5046cdd5c2d3864aa39f2 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
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 <storchaka@gmail.com>
---
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.

View File

@@ -0,0 +1,190 @@
From 9043edabc7e2f0dd655146e0a4571e2a0b2906af Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Fri, 13 Jun 2025 19:57:48 +0300
Subject: [PATCH] gh-135462: Fix quadratic complexity in processing special
input in HTMLParser (GH-135464)
End-of-file errors are now handled according to the HTML5 specs --
comments and declarations are automatically closed, tags are ignored.
(cherry picked from commit 6eb6c5dbfb528bd07d77b60fd71fd05d81d45c41)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
---
Lib/html/parser.py | 41 +++++---
Lib/test/test_htmlparser.py | 51 +++++++---
Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst | 4
3 files changed, 74 insertions(+), 22 deletions(-)
create mode 100644 Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst
Index: Python-3.11.13/Lib/html/parser.py
===================================================================
--- Python-3.11.13.orig/Lib/html/parser.py 2025-07-02 18:12:07.084569398 +0200
+++ Python-3.11.13/Lib/html/parser.py 2025-07-02 18:12:12.582519793 +0200
@@ -25,6 +25,7 @@
charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
starttagopen = re.compile('<[a-zA-Z]')
+endtagopen = re.compile('</[a-zA-Z]')
piclose = re.compile('>')
commentclose = re.compile(r'--\s*>')
# Note:
@@ -176,7 +177,7 @@
k = self.parse_pi(i)
elif startswith("<!", i):
k = self.parse_html_declaration(i)
- elif (i + 1) < n:
+ elif (i + 1) < n or end:
self.handle_data("<")
k = i + 1
else:
@@ -184,17 +185,35 @@
if k < 0:
if not end:
break
- k = rawdata.find('>', i + 1)
- if k < 0:
- k = rawdata.find('<', i + 1)
- if k < 0:
- k = i + 1
+ if starttagopen.match(rawdata, i): # < + letter
+ pass
+ elif startswith("</", i):
+ if i + 2 == n:
+ self.handle_data("</")
+ elif endtagopen.match(rawdata, i): # </ + letter
+ pass
+ else:
+ # bogus comment
+ self.handle_comment(rawdata[i+2:])
+ elif startswith("<!--", i):
+ j = n
+ for suffix in ("--!", "--", "-"):
+ if rawdata.endswith(suffix, i+4):
+ j -= len(suffix)
+ break
+ self.handle_comment(rawdata[i+4:j])
+ elif startswith("<![CDATA[", i):
+ self.unknown_decl(rawdata[i+3:])
+ elif rawdata[i:i+9].lower() == '<!doctype':
+ self.handle_decl(rawdata[i+2:])
+ elif startswith("<!", i):
+ # bogus comment
+ self.handle_comment(rawdata[i+2:])
+ elif startswith("<?", i):
+ self.handle_pi(rawdata[i+2:])
else:
- k += 1
- if self.convert_charrefs and not self.cdata_elem:
- self.handle_data(unescape(rawdata[i:k]))
- else:
- self.handle_data(rawdata[i:k])
+ raise AssertionError("we should not get here!")
+ k = n
i = self.updatepos(i, k)
elif startswith("&#", i):
match = charref.match(rawdata, i)
Index: Python-3.11.13/Lib/test/test_htmlparser.py
===================================================================
--- Python-3.11.13.orig/Lib/test/test_htmlparser.py 2025-07-02 18:12:08.523658593 +0200
+++ Python-3.11.13/Lib/test/test_htmlparser.py 2025-07-02 18:13:32.674943007 +0200
@@ -4,6 +4,8 @@
import pprint
import unittest
+from test import support
+
class EventCollector(html.parser.HTMLParser):
@@ -391,28 +393,34 @@
('data', '<'),
('starttag', 'bc<', [('a', None)]),
('endtag', 'html'),
- ('data', '\n<img src="URL>'),
- ('comment', '/img'),
- ('endtag', 'html<')])
+ ('data', '\n')])
def test_starttag_junk_chars(self):
+ self._run_check("<", [('data', '<')])
+ self._run_check("<>", [('data', '<>')])
+ self._run_check("< >", [('data', '< >')])
+ self._run_check("< ", [('data', '< ')])
self._run_check("</>", [])
+ self._run_check("<$>", [('data', '<$>')])
self._run_check("</$>", [('comment', '$')])
self._run_check("</", [('data', '</')])
- self._run_check("</a", [('data', '</a')])
+ self._run_check("</a", [])
+ self._run_check("</ a>", [('endtag', 'a')])
+ self._run_check("</ a", [('comment', ' a')])
self._run_check("<a<a>", [('starttag', 'a<a', [])])
self._run_check("</a<a>", [('endtag', 'a<a')])
- self._run_check("<!", [('data', '<!')])
- self._run_check("<a", [('data', '<a')])
- self._run_check("<a foo='bar'", [('data', "<a foo='bar'")])
- self._run_check("<a foo='bar", [('data', "<a foo='bar")])
- self._run_check("<a foo='>'", [('data', "<a foo='>'")])
- self._run_check("<a foo='>", [('data', "<a foo='>")])
+ self._run_check("<!", [('comment', '')])
+ self._run_check("<a", [])
+ self._run_check("<a foo='bar'", [])
+ self._run_check("<a foo='bar", [])
+ self._run_check("<a foo='>'", [])
+ self._run_check("<a foo='>", [])
self._run_check("<a$>", [('starttag', 'a$', [])])
self._run_check("<a$b>", [('starttag', 'a$b', [])])
self._run_check("<a$b/>", [('startendtag', 'a$b', [])])
self._run_check("<a$b >", [('starttag', 'a$b', [])])
self._run_check("<a$b />", [('startendtag', 'a$b', [])])
+ self._run_check("</a$b>", [('endtag', 'a$b')])
def test_slashes_in_starttag(self):
self._run_check('<a foo="var"/>', [('startendtag', 'a', [('foo', 'var')])])
@@ -549,8 +557,9 @@
('comment', ' -- close enough --'),
('comment', ''),
('comment', '<-- this was an empty comment'),
- ('comment', '!! another bogus comment !!!'),
+ ('comment', '!! another bogus comment !!!')
]
+
self._run_check(html, expected)
def test_broken_condcoms(self):
@@ -598,6 +607,26 @@
('endtag', 'a'), ('data', ' bar & baz')]
)
+ @support.requires_resource('cpu')
+ def test_eof_no_quadratic_complexity(self):
+ # Each of these examples used to take about an hour.
+ # Now they take a fraction of a second.
+ def check(source):
+ parser = html.parser.HTMLParser()
+ parser.feed(source)
+ parser.close()
+ n = 120_000
+ check("<a " * n)
+ check("<a a=" * n)
+ check("</a " * 14 * n)
+ check("</a a=" * 11 * n)
+ check("<!--" * 4 * n)
+ check("<!" * 60 * n)
+ check("<?" * 19 * n)
+ check("</$" * 15 * n)
+ check("<![CDATA[" * 9 * n)
+ check("<!doctype" * 35 * n)
+
class AttributesTestCase(TestCaseBase):
Index: Python-3.11.13/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ Python-3.11.13/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst 2025-07-02 18:12:12.583386736 +0200
@@ -0,0 +1,4 @@
+Fix quadratic complexity in processing specially crafted input in
+:class:`html.parser.HTMLParser`. End-of-file errors are now handled according
+to the HTML5 specs -- comments and declarations are automatically closed,
+tags are ignored.

View File

@@ -1,359 +0,0 @@
From e717839989908ecea9c1c8f3bd17ec9fb1ac8963 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
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 <storchaka@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
---
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`.

View File

@@ -0,0 +1,212 @@
From cb3519590c62f9b1abf7f31b92ec37d4b725ce15 Mon Sep 17 00:00:00 2001
From: Alexander Urieles <aeurielesn@users.noreply.github.com>
Date: Mon, 28 Jul 2025 17:37:26 +0200
Subject: [PATCH] gh-130577: tarfile now validates archives to ensure member
offsets are non-negative (GH-137027) (cherry picked from commit
7040aa54f14676938970e10c5f74ea93cd56aa38)
Co-authored-by: Alexander Urieles <aeurielesn@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
---
Lib/tarfile.py | 3
Lib/test/test_tarfile.py | 156 ++++++++++
Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst | 3
3 files changed, 162 insertions(+)
create mode 100644 Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
Index: Python-3.11.13/Lib/tarfile.py
===================================================================
--- Python-3.11.13.orig/Lib/tarfile.py 2025-08-01 22:21:29.158050900 +0200
+++ Python-3.11.13/Lib/tarfile.py 2025-08-01 22:21:33.121079687 +0200
@@ -1613,6 +1613,9 @@
"""Round up a byte count by BLOCKSIZE and return it,
e.g. _block(834) => 1024.
"""
+ # Only non-negative offsets are allowed
+ if count < 0:
+ raise InvalidHeaderError("invalid offset")
blocks, remainder = divmod(count, BLOCKSIZE)
if remainder:
blocks += 1
Index: Python-3.11.13/Lib/test/test_tarfile.py
===================================================================
--- Python-3.11.13.orig/Lib/test/test_tarfile.py 2025-08-01 22:21:30.644301786 +0200
+++ Python-3.11.13/Lib/test/test_tarfile.py 2025-08-01 22:21:33.121718600 +0200
@@ -50,6 +50,7 @@
xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
tmpname = os.path.join(TEMPDIR, "tmp.tar")
dotlessname = os.path.join(TEMPDIR, "testtar")
+SPACE = b" "
sha256_regtype = (
"e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
@@ -4386,6 +4387,161 @@
ar.extractall(self.testdir, filter='fully_trusted')
+class OffsetValidationTests(unittest.TestCase):
+ tarname = tmpname
+ invalid_posix_header = (
+ # name: 100 bytes
+ tarfile.NUL * tarfile.LENGTH_NAME
+ # mode, space, null terminator: 8 bytes
+ + b"000755" + SPACE + tarfile.NUL
+ # uid, space, null terminator: 8 bytes
+ + b"000001" + SPACE + tarfile.NUL
+ # gid, space, null terminator: 8 bytes
+ + b"000001" + SPACE + tarfile.NUL
+ # size, space: 12 bytes
+ + b"\xff" * 11 + SPACE
+ # mtime, space: 12 bytes
+ + tarfile.NUL * 11 + SPACE
+ # chksum: 8 bytes
+ + b"0011407" + tarfile.NUL
+ # type: 1 byte
+ + tarfile.REGTYPE
+ # linkname: 100 bytes
+ + tarfile.NUL * tarfile.LENGTH_LINK
+ # magic: 6 bytes, version: 2 bytes
+ + tarfile.POSIX_MAGIC
+ # uname: 32 bytes
+ + tarfile.NUL * 32
+ # gname: 32 bytes
+ + tarfile.NUL * 32
+ # devmajor, space, null terminator: 8 bytes
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
+ # devminor, space, null terminator: 8 bytes
+ + tarfile.NUL * 6 + SPACE + tarfile.NUL
+ # prefix: 155 bytes
+ + tarfile.NUL * tarfile.LENGTH_PREFIX
+ # padding: 12 bytes
+ + tarfile.NUL * 12
+ )
+ invalid_gnu_header = (
+ # name: 100 bytes
+ tarfile.NUL * tarfile.LENGTH_NAME
+ # mode, null terminator: 8 bytes
+ + b"0000755" + tarfile.NUL
+ # uid, null terminator: 8 bytes
+ + b"0000001" + tarfile.NUL
+ # gid, space, null terminator: 8 bytes
+ + b"0000001" + tarfile.NUL
+ # size, space: 12 bytes
+ + b"\xff" * 11 + SPACE
+ # mtime, space: 12 bytes
+ + tarfile.NUL * 11 + SPACE
+ # chksum: 8 bytes
+ + b"0011327" + tarfile.NUL
+ # type: 1 byte
+ + tarfile.REGTYPE
+ # linkname: 100 bytes
+ + tarfile.NUL * tarfile.LENGTH_LINK
+ # magic: 8 bytes
+ + tarfile.GNU_MAGIC
+ # uname: 32 bytes
+ + tarfile.NUL * 32
+ # gname: 32 bytes
+ + tarfile.NUL * 32
+ # devmajor, null terminator: 8 bytes
+ + tarfile.NUL * 8
+ # devminor, null terminator: 8 bytes
+ + tarfile.NUL * 8
+ # padding: 167 bytes
+ + tarfile.NUL * 167
+ )
+ invalid_v7_header = (
+ # name: 100 bytes
+ tarfile.NUL * tarfile.LENGTH_NAME
+ # mode, space, null terminator: 8 bytes
+ + b"000755" + SPACE + tarfile.NUL
+ # uid, space, null terminator: 8 bytes
+ + b"000001" + SPACE + tarfile.NUL
+ # gid, space, null terminator: 8 bytes
+ + b"000001" + SPACE + tarfile.NUL
+ # size, space: 12 bytes
+ + b"\xff" * 11 + SPACE
+ # mtime, space: 12 bytes
+ + tarfile.NUL * 11 + SPACE
+ # chksum: 8 bytes
+ + b"0010070" + tarfile.NUL
+ # type: 1 byte
+ + tarfile.REGTYPE
+ # linkname: 100 bytes
+ + tarfile.NUL * tarfile.LENGTH_LINK
+ # padding: 255 bytes
+ + tarfile.NUL * 255
+ )
+ valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT)
+ data_block = b"\xff" * tarfile.BLOCKSIZE
+
+ def _write_buffer(self, buffer):
+ with open(self.tarname, "wb") as f:
+ f.write(buffer)
+
+ def _get_members(self, ignore_zeros=None):
+ with open(self.tarname, "rb") as f:
+ with tarfile.open(
+ mode="r", fileobj=f, ignore_zeros=ignore_zeros
+ ) as tar:
+ return tar.getmembers()
+
+ def _assert_raises_read_error_exception(self):
+ with self.assertRaisesRegex(
+ tarfile.ReadError, "file could not be opened successfully"
+ ):
+ self._get_members()
+
+ def test_invalid_offset_header_validations(self):
+ for tar_format, invalid_header in (
+ ("posix", self.invalid_posix_header),
+ ("gnu", self.invalid_gnu_header),
+ ("v7", self.invalid_v7_header),
+ ):
+ with self.subTest(format=tar_format):
+ self._write_buffer(invalid_header)
+ self._assert_raises_read_error_exception()
+
+ def test_early_stop_at_invalid_offset_header(self):
+ buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header
+ self._write_buffer(buffer)
+ members = self._get_members()
+ self.assertEqual(len(members), 1)
+ self.assertEqual(members[0].name, "filename")
+ self.assertEqual(members[0].offset, 0)
+
+ def test_ignore_invalid_archive(self):
+ # 3 invalid headers with their respective data
+ buffer = (self.invalid_gnu_header + self.data_block) * 3
+ self._write_buffer(buffer)
+ members = self._get_members(ignore_zeros=True)
+ self.assertEqual(len(members), 0)
+
+ def test_ignore_invalid_offset_headers(self):
+ for first_block, second_block, expected_offset in (
+ (
+ (self.valid_gnu_header),
+ (self.invalid_gnu_header + self.data_block),
+ 0,
+ ),
+ (
+ (self.invalid_gnu_header + self.data_block),
+ (self.valid_gnu_header),
+ 1024,
+ ),
+ ):
+ self._write_buffer(first_block + second_block)
+ members = self._get_members(ignore_zeros=True)
+ self.assertEqual(len(members), 1)
+ self.assertEqual(members[0].name, "filename")
+ self.assertEqual(members[0].offset, expected_offset)
+
+
def setUpModule():
os_helper.unlink(TEMPDIR)
os.makedirs(TEMPDIR)
Index: Python-3.11.13/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ Python-3.11.13/Misc/NEWS.d/next/Library/2025-07-23-00-35-29.gh-issue-130577.c7EITy.rst 2025-08-01 22:21:33.122108946 +0200
@@ -0,0 +1,3 @@
+:mod:`tarfile` now validates archives to ensure member offsets are
+non-negative. (Contributed by Alexander Enrique Urieles Nieto in
+:gh:`130577`.)

BIN
Python-3.11.13.tar.xz LFS Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": {"certificate": {"rawBytes": "MIICzjCCAlSgAwIBAgIUfnOGm4U1QCsCXWiDvPy5Tgni2HUwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwNjAzMTkyNzM1WhcNMjUwNjAzMTkzNzM1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpTsf/wrCdu4Domf4WOtO4CLkj51wmj4iesYv5N6DYhghPjqQFwGYI9gFc/WX6QMIWh5YHU2NGxrmM7KfbAYzz6OCAXMwggFvMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUkQ2/O4Fivj1bTq7NTQczm1RdtYAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wIgYDVR0RAQH/BBgwFoEUcGFibG9nc2FsQHB5dGhvbi5vcmcwKQYKKwYBBAGDvzABAQQbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMCsGCisGAQQBg78wAQgEHQwbaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tMIGLBgorBgEEAdZ5AgQCBH0EewB5AHcA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGXN0NwGwAABAMASDBGAiEA6pD8PjS+5z2SQre/NS/wOdFSjVMsxvtfF6A1jg+1T3YCIQDC44S/Z3c0dNddM7EkE+A3j7Vft3hqRUoFkNe4U6g5qTAKBggqhkjOPQQDAwNoADBlAjA6lBI2r3KCZFc+2affpH3S3Xj3gMOKh8Lr5Z7TgkGp3Q6QsnExGmJJ0leXhqH6rQkCMQDfxk/6DyhbO7KTrIUfmrbZoa7dV75cresJS69Xk67XN57qsqY52DZj9o4fbUIw4ro="}, "tlogEntries": [{"logIndex": "228953871", "logId": {"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion": {"kind": "hashedrekord", "version": "0.0.1"}, "integratedTime": "1748978856", "inclusionPromise": {"signedEntryTimestamp": "MEUCIQC9nXmfcRqyOL2Zmw1zI7+kulTbmDE3Yfzew81mXJGU4QIgF8Uhdg2uzttSA6erOuEchX68PCyJ0cVFHE0XJX2+ZfE="}, "inclusionProof": {"logIndex": "107049609", "rootHash": "Ilofw5POqC/C3zqfrdMQP1DyhNW+UfB9fHdjrbK6qaM=", "treeSize": "107049610", "hashes": ["AcD1iyjU7nuIPqAq29ynz7PEdq6zPXglj6e2tkH+/do=", "1BNDCN01B3dbUo/TfLaQgKIYTvPyrkcrHKd69GxuF2E=", "t59A0CV2pHM2S9AgZgcEA6FbXhgNZGo0jMRIXHiqsJ0=", "bCrkgWpJ8MBic+mIfCRsKi+5XAMqgM8Lc6G0LLfzZ7M=", "4iwdOrGkcqdN0qqZUx/gv8a8qpLMqVj8aXRVmhQ558c=", "mAX/zvx1jR0ujLtDApsQpHyxmoDGidClHMOn0BX1aQA=", "u5LKLBPTYgXZg0fBi6/8LuEeNy3EBAxJF0AkkB4Co6E=", "SPUVncwJRVX/n/RICCYqLpAzraqx7S0eMdXRr1RLRgg=", "uEJFtwcGQJMd9kjQhkXb7gl2WD3WMElCc15uDFvFGxs=", "VdOKzpQhJlpXgijzXANf/hNlje1G/N1kUuVnKNskkso=", "mta5fH/gFwxJ/0fT8yGpn3sFCY0G1RY555Iflm0LInM=", "7v8qPHNDLerpduaMx06eb/MwgoQwczTn/cYGKX/9wZ4="], "checkpoint": {"envelope": "rekor.sigstore.dev - 1193050959916656506\n107049610\nIlofw5POqC/C3zqfrdMQP1DyhNW+UfB9fHdjrbK6qaM=\n\n\u2014 rekor.sigstore.dev wNI9ajBGAiEAjtzTnsnrGx0G3Dg99s89cPUh6EA+cxkicQ9j4qYU60wCIQCKcAL4kdakbq2JrBVgk7bRNf3FoJRrEI6SCjv16f7Crg==\n"}}, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI4ZmI1ZjlmYmM3NjA5ZmE4MjJjYjMxNTQ5ODg0NTc1ZGI3ZmQ5NjU3Y2JmZmI4OTUxMGI1ZDc5NzU5NjNhODNhIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUM5Q1JZRjNSWGUzdDNxQlBJd2UrR3pMMTJCOXVLTjIrRFpWa2JjZW1FTS93SWdPMDFKaVhnbUJxZEN5RVhoM05JUEt5QlRBb2hpcjZHTkhZdXhiSUxKNDlRPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVTjZha05EUVd4VFowRjNTVUpCWjBsVlptNVBSMjAwVlRGUlEzTkRXRmRwUkhaUWVUVlVaMjVwTWtoVmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFZkMDVxUVhwTlZHdDVUbnBOTVZkb1kwNU5hbFYzVG1wQmVrMVVhM3BPZWsweFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZ3VkhObUwzZHlRMlIxTkVSdmJXWTBWMDkwVHpSRFRHdHFOVEYzYldvMGFXVnpXWFlLTlU0MlJGbG9aMmhRYW5GUlJuZEhXVWs1WjBaakwxZFlObEZOU1Zkb05WbElWVEpPUjNoeWJVMDNTMlppUVZsNmVqWlBRMEZZVFhkblowWjJUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZyVVRJdkNrODBSbWwyYWpGaVZIRTNUbFJSWTNwdE1WSmtkRmxCZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDBsbldVUldVakJTUVZGSUwwSkNaM2RHYjBWVlkwZEdhV0pIT1c1ak1rWnpVVWhDTldSSGFIWmlhVFYyWTIxamQwdFJXVXRMZDFsQ1FrRkhSQXAyZWtGQ1FWRlJZbUZJVWpCalNFMDJUSGs1YUZreVRuWmtWelV3WTNrMWJtSXlPVzVpUjFWMVdUSTVkRTFEYzBkRGFYTkhRVkZSUW1jM09IZEJVV2RGQ2toUmQySmhTRkl3WTBoTk5reDVPV2haTWs1MlpGYzFNR041Tlc1aU1qbHVZa2RWZFZreU9YUk5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVVS1pYZENOVUZJWTBFelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIxaE9NRTUzUjNkQlFRcENRVTFCVTBSQ1IwRnBSVUUyY0VRNFVHcFRLelY2TWxOUmNtVXZUbE12ZDA5a1JsTnFWazF6ZUhaMFprWTJRVEZxWnlzeFZETlpRMGxSUkVNME5GTXZDbG96WXpCa1RtUmtUVGRGYTBVclFUTnFOMVptZEROb2NWSlZiMFpyVG1VMFZUWm5OWEZVUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFRVFlLYkVKSk1uSXpTME5hUm1Nck1tRm1abkJJTTFNeldHb3paMDFQUzJnNFRISTFXamRVWjJ0SGNETlJObEZ6YmtWNFIyMUtTakJzWlZob2NVZzJjbEZyUXdwTlVVUm1lR3N2TmtSNWFHSlBOMHRVY2tsVlptMXlZbHB2WVRka1ZqYzFZM0psYzBwVE5qbFlhelkzV0U0MU4zRnpjVmsxTWtSYWFqbHZOR1ppVlVsM0NqUnliejBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0="}], "timestampVerificationData": {}}, "messageSignature": {"messageDigest": {"algorithm": "SHA2_256", "digest": "j7X5+8dgn6giyzFUmIRXXbf9llfL/7iVELXXl1ljqDo="}, "signature": "MEUCIQC9CRYF3RXe3t3qBPIwe+GzL12B9uKN2+DZVkbcemEM/wIgO01JiXgmBqdCyEXh3NIPKyBTAohir6GNHYuxbILJ49Q="}}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -3,11 +3,9 @@
Misc/NEWS | 2 +-
2 files changed, 1 insertion(+), 4 deletions(-)
Index: Python-3.11.14/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
@@ -43,7 +43,6 @@
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -43,7 +43,6 @@ General Options
See :data:`sys.int_info.bits_per_digit <sys.int_info>`.
@@ -15,7 +13,7 @@ Index: Python-3.11.14/Doc/using/configure.rst
.. option:: --with-cxx-main=COMPILER
Compile the Python ``main()`` function and link Python executable with C++
@@ -529,13 +528,11 @@
@@ -529,13 +528,11 @@ macOS Options
See ``Mac/README.rst``.
@@ -29,11 +27,9 @@ 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
===================================================================
--- 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 @@
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -9911,7 +9911,7 @@ C API
- bpo-40939: Removed documentation for the removed ``PyParser_*`` C API.
- bpo-43795: The list in :ref:`limited-api-list` now shows the public name

View File

@@ -1,36 +0,0 @@
From 19b61747df3d62c822285c488753d6fbdf91e3ac Mon Sep 17 00:00:00 2001
From: Daniel Garcia Moreno <daniel.garcia@suse.com>
Date: Tue, 23 Sep 2025 10:20:16 +0200
Subject: [PATCH 1/2] gh-139257: Support docutils >= 0.22
---
Doc/tools/extensions/pyspecific.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
Index: Python-3.11.13/Doc/tools/extensions/pyspecific.py
===================================================================
--- Python-3.11.13.orig/Doc/tools/extensions/pyspecific.py 2025-06-03 20:38:25.000000000 +0200
+++ Python-3.11.13/Doc/tools/extensions/pyspecific.py 2025-09-30 18:20:10.096471679 +0200
@@ -48,11 +48,21 @@
SOURCE_URI = 'https://github.com/python/cpython/tree/3.11/%s'
# monkey-patch reST parser to disable alphabetic and roman enumerated lists
+def _disable_alphabetic_and_roman(text):
+ try:
+ # docutils >= 0.22
+ from docutils.parsers.rst.states import InvalidRomanNumeralError
+ raise InvalidRomanNumeralError(text)
+ except ImportError:
+ # docutils < 0.22
+ return None
+
+
from docutils.parsers.rst.states import Body
Body.enum.converters['loweralpha'] = \
Body.enum.converters['upperalpha'] = \
Body.enum.converters['lowerroman'] = \
- Body.enum.converters['upperroman'] = lambda x: None
+ Body.enum.converters['upperroman'] = _disable_alphabetic_and_roman
# monkey-patch the productionlist directive to allow hyphens in group names
# https://github.com/sphinx-doc/sphinx/issues/11854

View File

@@ -1,95 +1,3 @@
-------------------------------------------------------------------
Thu Dec 18 10:33:44 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
- Add CVE-2025-13836-http-resp-cont-len.patch (bsc#1254400,
CVE-2025-13836) to prevent reading an HTTP response from
a server, if no read amount is specified, with using
Content-Length per default as the length.
- Add CVE-2025-12084-minidom-quad-search.patch prevent quadratic
behavior in node ID cache clearing (CVE-2025-12084,
bsc#1254997).
- Add CVE-2025-13837-plistlib-mailicious-length.patch protect
against OOM when loading malicious content (CVE-2025-13837,
bsc#1254401).
-------------------------------------------------------------------
Thu Nov 13 17:13:03 UTC 2025 - Matej Cepl <mcepl@cepl.eu>
- Add CVE-2025-6075-expandvars-perf-degrad.patch avoid simple
quadratic complexity vulnerabilities of os.path.expandvars()
(CVE-2025-6075, bsc#1252974).
- Readjusted patches:
- CVE-2023-52425-libexpat-2.6.0-backport.patch
- CVE-2023-52425-remove-reparse_deferral-tests.patch
- fix_configure_rst.patch
- skip_if_buildbot-extend.patch
-------------------------------------------------------------------
Wed Oct 15 08:52:35 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
- Update to 3.11.14:
- Security
- gh-139700: Check consistency of the zip64 end of central
directory record. Support records with “zip64 extensible data”
if there are no bytes prepended to the ZIP file
(CVE-2025-8291, bsc#1251305).
- gh-139400: xml.parsers.expat: Make sure that parent Expat
parsers are only garbage-collected once they are no longer
referenced by subparsers created by
ExternalEntityParserCreate(). Patch by Sebastian Pipping.
- gh-135661: Fix parsing start and end tags in
html.parser.HTMLParser according to the HTML5 standard.
* Whitespaces no longer accepted between </ and the tag name. E.g.
</ script> does not end the script section.
* Vertical tabulation (\v) and non-ASCII whitespaces no longer
recognized as whitespaces. The only whitespaces are \t\n\r\f and
space.
* Null character (U+0000) no longer ends the tag name.
* Attributes and slashes after the tag name in end tags are now
ignored, instead of terminating after the first > in quoted
attribute value. E.g. </script/foo=">"/>.
* Multiple slashes and whitespaces between the last attribute and
closing > are now ignored in both start and end tags. E.g. <a
foo=bar/ //>.
* Multiple = between attribute name and value are no longer
collapsed. E.g. <a foo==bar> produces attribute “foo” with value
“=bar”.
- gh-135661: Fix CDATA section parsing in html.parser.HTMLParser
according to the HTML5 standard: ] ]> and ]] > no longer end the
CDATA section. Add private method _set_support_cdata() which can
be used to specify how to parse <[CDATA[ — as a CDATA section in
foreign content (SVG or MathML) or as a bogus comment in the
HTML namespace.
- gh-102555: Fix comment parsing in html.parser.HTMLParser
according to the HTML5 standard. --!> now ends the comment. -- >
no longer ends the comment. Support abnormally ended empty
comments <--> and <--->.
- gh-135462: Fix quadratic complexity in processing specially
crafted input in html.parser.HTMLParser. End-of-file errors are
now handled according to the HTML5 specs comments and
declarations are automatically closed, tags are ignored.
- gh-118350: Fix support of escapable raw text mode (elements
“textarea” and “title”) in html.parser.HTMLParser.
- gh-86155: html.parser.HTMLParser.close() no longer loses data
when the <script> tag is not closed. Patch by Waylan Limberg.
- Library
- gh-139312: Upgrade bundled libexpat to 2.7.3
- gh-138998: Update bundled libexpat to 2.7.2
- gh-130577: tarfile now validates archives to ensure member
offsets are non-negative. (Contributed by Alexander Enrique
Urieles Nieto in gh-130577.)
- gh-135374: Update the bundled copy of setuptools to 79.0.1.
- Drop upstreamed patches:
- CVE-2025-8194-tarfile-no-neg-offsets.patch
- CVE-2025-6069-quad-complex-HTMLParser.patch
-------------------------------------------------------------------
Mon Sep 29 06:52:07 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
- Add gh139257-Support-docutils-0.22.patch to fix build with latest
docutils (>=0.22) gh#python/cpython#139257
-------------------------------------------------------------------
Fri Sep 19 14:38:03 UTC 2025 - Dominique Leuenberger <dimstar@opensuse.org>

View File

@@ -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.13
Release: 0
Summary: Python 3 Interpreter
License: Python-2.0
@@ -186,20 +186,12 @@ Patch19: bso1227999-reproducible-builds.patch
Patch22: gh120226-fix-sendfile-test-kernel-610.patch
# PATCH-FIX-UPSTREAM Add platform triplets for 64-bit LoongArch gh#python/cpython#30939 glaubitz@suse.com
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-6069-quad-complex-HTMLParser.patch bsc#1244705 mcepl@suse.com
# avoid quadratic complexity when processing malformed inputs with HTMLParser
Patch25: CVE-2025-6069-quad-complex-HTMLParser.patch
# PATCH-FIX-UPSTREAM CVE-2025-8194-tarfile-no-neg-offsets.patch bsc#1247249 mcepl@suse.com
# tarfile now validates archives to ensure member offsets are non-negative
Patch26: CVE-2025-8194-tarfile-no-neg-offsets.patch
BuildRequires: autoconf-archive
BuildRequires: automake
BuildRequires: crypto-policies-scripts

View File

@@ -2,11 +2,9 @@
Lib/test/support/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Index: Python-3.11.14/Lib/test/support/__init__.py
===================================================================
--- Python-3.11.14.orig/Lib/test/support/__init__.py 2025-11-15 19:14:54.049952478 +0100
+++ Python-3.11.14/Lib/test/support/__init__.py 2025-11-15 19:15:08.449938538 +0100
@@ -394,7 +394,7 @@
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -384,7 +384,7 @@ def skip_if_buildbot(reason=None):
if not reason:
reason = 'not suitable for buildbots'
try: