forked from pool/python311
Compare commits
7 Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
162a9695a4
|
|||
|
48953809e4
|
|||
|
06d1a72674
|
|||
| c61cd14450 | |||
| c3b20ea06a | |||
| 4fd1005be3 | |||
| 09df53c2ab |
@@ -6,10 +6,10 @@
|
|||||||
Lib/test/test_xml_etree.py | 12 ------------
|
Lib/test/test_xml_etree.py | 12 ------------
|
||||||
5 files changed, 37 insertions(+), 44 deletions(-)
|
5 files changed, 37 insertions(+), 44 deletions(-)
|
||||||
|
|
||||||
Index: Python-3.11.12/Lib/test/support/__init__.py
|
Index: Python-3.11.14/Lib/test/support/__init__.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.11.12.orig/Lib/test/support/__init__.py 2025-04-11 10:52:43.191010503 +0200
|
--- Python-3.11.14.orig/Lib/test/support/__init__.py 2025-11-15 19:15:08.449938538 +0100
|
||||||
+++ Python-3.11.12/Lib/test/support/__init__.py 2025-04-11 10:52:44.802161741 +0200
|
+++ Python-3.11.14/Lib/test/support/__init__.py 2025-11-15 19:15:12.859120260 +0100
|
||||||
@@ -8,6 +8,7 @@
|
@@ -8,6 +8,7 @@
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
@@ -27,7 +27,7 @@ Index: Python-3.11.12/Lib/test/support/__init__.py
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -2244,6 +2245,17 @@
|
@@ -2279,6 +2280,17 @@
|
||||||
}
|
}
|
||||||
return ignored
|
return ignored
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ Index: Python-3.11.12/Lib/test/support/__init__.py
|
|||||||
+fails_with_expat_2_6_0 = (unittest.expectedFailure
|
+fails_with_expat_2_6_0 = (unittest.expectedFailure
|
||||||
+ if is_expat_2_6_0
|
+ if is_expat_2_6_0
|
||||||
+ else lambda test: test)
|
+ else lambda test: test)
|
||||||
Index: Python-3.11.12/Lib/test/test_minidom.py
|
Index: Python-3.11.14/Lib/test/test_minidom.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.11.12.orig/Lib/test/test_minidom.py 2025-04-11 10:52:21.907086938 +0200
|
--- Python-3.11.14.orig/Lib/test/test_minidom.py 2025-11-15 19:14:53.915952608 +0100
|
||||||
+++ Python-3.11.12/Lib/test/test_minidom.py 2025-04-11 10:52:44.802522893 +0200
|
+++ Python-3.11.14/Lib/test/test_minidom.py 2025-11-15 19:15:12.859877278 +0100
|
||||||
@@ -6,7 +6,6 @@
|
@@ -6,7 +6,6 @@
|
||||||
from test import support
|
from test import support
|
||||||
import unittest
|
import unittest
|
||||||
@@ -94,10 +94,10 @@ Index: Python-3.11.12/Lib/test/test_minidom.py
|
|||||||
parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
|
parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
|
||||||
|
|
||||||
def testDocRemoveChild(self):
|
def testDocRemoveChild(self):
|
||||||
Index: Python-3.11.12/Lib/test/test_pyexpat.py
|
Index: Python-3.11.14/Lib/test/test_pyexpat.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.11.12.orig/Lib/test/test_pyexpat.py 2025-04-11 10:52:22.076696906 +0200
|
--- Python-3.11.14.orig/Lib/test/test_pyexpat.py 2025-11-15 19:14:53.915952608 +0100
|
||||||
+++ Python-3.11.12/Lib/test/test_pyexpat.py 2025-04-11 10:52:44.803228085 +0200
|
+++ Python-3.11.14/Lib/test/test_pyexpat.py 2025-11-15 19:15:12.860334045 +0100
|
||||||
@@ -14,8 +14,7 @@
|
@@ -14,8 +14,7 @@
|
||||||
from xml.parsers import expat
|
from xml.parsers import expat
|
||||||
from xml.parsers.expat import errors
|
from xml.parsers.expat import errors
|
||||||
@@ -108,7 +108,7 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
|
|||||||
|
|
||||||
class SetAttributeTest(unittest.TestCase):
|
class SetAttributeTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -770,9 +769,8 @@
|
@@ -806,9 +805,8 @@
|
||||||
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
|
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
|
||||||
|
|
||||||
def test_reparse_deferral_enabled(self):
|
def test_reparse_deferral_enabled(self):
|
||||||
@@ -120,7 +120,7 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
|
|||||||
|
|
||||||
started = []
|
started = []
|
||||||
|
|
||||||
@@ -801,9 +799,9 @@
|
@@ -837,9 +835,9 @@
|
||||||
|
|
||||||
parser = expat.ParserCreate()
|
parser = expat.ParserCreate()
|
||||||
parser.StartElementHandler = start_element
|
parser.StartElementHandler = start_element
|
||||||
@@ -132,10 +132,10 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
|
|||||||
|
|
||||||
for chunk in (b'<doc', b'/>'):
|
for chunk in (b'<doc', b'/>'):
|
||||||
parser.Parse(chunk, False)
|
parser.Parse(chunk, False)
|
||||||
Index: Python-3.11.12/Lib/test/test_sax.py
|
Index: Python-3.11.14/Lib/test/test_sax.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.11.12.orig/Lib/test/test_sax.py 2025-04-11 10:52:22.111440337 +0200
|
--- Python-3.11.14.orig/Lib/test/test_sax.py 2025-11-15 19:14:53.915952608 +0100
|
||||||
+++ Python-3.11.12/Lib/test/test_sax.py 2025-04-11 10:52:44.803567098 +0200
|
+++ Python-3.11.14/Lib/test/test_sax.py 2025-11-15 19:15:12.860746114 +0100
|
||||||
@@ -19,13 +19,11 @@
|
@@ -19,13 +19,11 @@
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO
|
||||||
import codecs
|
import codecs
|
||||||
@@ -187,10 +187,10 @@ Index: Python-3.11.12/Lib/test/test_sax.py
|
|||||||
|
|
||||||
self.assertFalse(parser._parser.GetReparseDeferralEnabled())
|
self.assertFalse(parser._parser.GetReparseDeferralEnabled())
|
||||||
|
|
||||||
Index: Python-3.11.12/Lib/test/test_xml_etree.py
|
Index: Python-3.11.14/Lib/test/test_xml_etree.py
|
||||||
===================================================================
|
===================================================================
|
||||||
--- Python-3.11.12.orig/Lib/test/test_xml_etree.py 2025-04-11 10:52:22.425637912 +0200
|
--- Python-3.11.14.orig/Lib/test/test_xml_etree.py 2025-11-15 19:14:53.915952608 +0100
|
||||||
+++ Python-3.11.12/Lib/test/test_xml_etree.py 2025-04-11 10:52:44.804234785 +0200
|
+++ Python-3.11.14/Lib/test/test_xml_etree.py 2025-11-15 19:15:12.861491049 +0100
|
||||||
@@ -13,7 +13,6 @@
|
@@ -13,7 +13,6 @@
|
||||||
import operator
|
import operator
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
Lib/test/test_xml_etree.py | 2 ++
|
Lib/test/test_xml_etree.py | 2 ++
|
||||||
3 files changed, 6 insertions(+)
|
3 files changed, 6 insertions(+)
|
||||||
|
|
||||||
--- a/Lib/test/test_pyexpat.py
|
Index: Python-3.11.14/Lib/test/test_pyexpat.py
|
||||||
+++ b/Lib/test/test_pyexpat.py
|
===================================================================
|
||||||
@@ -768,6 +768,7 @@ class ReparseDeferralTest(unittest.TestC
|
--- 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 @@
|
||||||
parser.SetReparseDeferralEnabled(True)
|
parser.SetReparseDeferralEnabled(True)
|
||||||
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
|
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
|
||||||
|
|
||||||
@@ -14,7 +16,7 @@
|
|||||||
def test_reparse_deferral_enabled(self):
|
def test_reparse_deferral_enabled(self):
|
||||||
if not is_expat_2_6_0:
|
if not is_expat_2_6_0:
|
||||||
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
||||||
@@ -791,6 +792,7 @@ class ReparseDeferralTest(unittest.TestC
|
@@ -827,6 +828,7 @@
|
||||||
|
|
||||||
self.assertEqual(started, ['doc'])
|
self.assertEqual(started, ['doc'])
|
||||||
|
|
||||||
@@ -22,9 +24,11 @@
|
|||||||
def test_reparse_deferral_disabled(self):
|
def test_reparse_deferral_disabled(self):
|
||||||
started = []
|
started = []
|
||||||
|
|
||||||
--- a/Lib/test/test_sax.py
|
Index: Python-3.11.14/Lib/test/test_sax.py
|
||||||
+++ b/Lib/test/test_sax.py
|
===================================================================
|
||||||
@@ -1213,6 +1213,7 @@ class ExpatReaderTest(XmlTestBase):
|
--- 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 @@
|
||||||
|
|
||||||
self.assertEqual(result.getvalue(), start + b"<doc>text</doc>")
|
self.assertEqual(result.getvalue(), start + b"<doc>text</doc>")
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@
|
|||||||
def test_flush_reparse_deferral_enabled(self):
|
def test_flush_reparse_deferral_enabled(self):
|
||||||
if not is_expat_2_6_0:
|
if not is_expat_2_6_0:
|
||||||
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
||||||
@@ -1238,6 +1239,7 @@ class ExpatReaderTest(XmlTestBase):
|
@@ -1238,6 +1239,7 @@
|
||||||
|
|
||||||
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
self.assertEqual(result.getvalue(), start + b"<doc></doc>")
|
||||||
|
|
||||||
@@ -40,9 +44,11 @@
|
|||||||
def test_flush_reparse_deferral_disabled(self):
|
def test_flush_reparse_deferral_disabled(self):
|
||||||
if not is_expat_2_6_0:
|
if not is_expat_2_6_0:
|
||||||
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
self.skipTest("Linked libexpat doesn't support reparse deferral")
|
||||||
--- a/Lib/test/test_xml_etree.py
|
Index: Python-3.11.14/Lib/test/test_xml_etree.py
|
||||||
+++ b/Lib/test/test_xml_etree.py
|
===================================================================
|
||||||
@@ -1620,6 +1620,7 @@ class XMLPullParserTest(unittest.TestCas
|
--- 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 @@
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
ET.XMLPullParser(events=('start', 'end', 'bogus'))
|
ET.XMLPullParser(events=('start', 'end', 'bogus'))
|
||||||
|
|
||||||
@@ -50,7 +56,7 @@
|
|||||||
def test_flush_reparse_deferral_enabled(self):
|
def test_flush_reparse_deferral_enabled(self):
|
||||||
parser = ET.XMLPullParser(events=('start', 'end'))
|
parser = ET.XMLPullParser(events=('start', 'end'))
|
||||||
|
|
||||||
@@ -1641,6 +1642,7 @@ class XMLPullParserTest(unittest.TestCas
|
@@ -1641,6 +1642,7 @@
|
||||||
|
|
||||||
self.assert_event_tags(parser, [('end', 'doc')])
|
self.assert_event_tags(parser, [('end', 'doc')])
|
||||||
|
|
||||||
|
|||||||
93
CVE-2025-12084-minidom-quad-search.patch
Normal file
93
CVE-2025-12084-minidom-quad-search.patch
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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.
|
||||||
155
CVE-2025-13836-http-resp-cont-len.patch
Normal file
155
CVE-2025-13836-http-resp-cont-len.patch
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
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.
|
||||||
160
CVE-2025-13837-plistlib-mailicious-length.patch
Normal file
160
CVE-2025-13837-plistlib-mailicious-length.patch
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
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.
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
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.
|
|
||||||
359
CVE-2025-6075-expandvars-perf-degrad.patch
Normal file
359
CVE-2025-6075-expandvars-perf-degrad.patch
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
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`.
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
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
BIN
Python-3.11.13.tar.xz
LFS
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
{"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="}}
|
|
||||||
BIN
Python-3.11.14.tar.xz
LFS
Normal file
BIN
Python-3.11.14.tar.xz
LFS
Normal file
Binary file not shown.
1
Python-3.11.14.tar.xz.sigstore
Normal file
1
Python-3.11.14.tar.xz.sigstore
Normal file
File diff suppressed because one or more lines are too long
@@ -3,9 +3,11 @@
|
|||||||
Misc/NEWS | 2 +-
|
Misc/NEWS | 2 +-
|
||||||
2 files changed, 1 insertion(+), 4 deletions(-)
|
2 files changed, 1 insertion(+), 4 deletions(-)
|
||||||
|
|
||||||
--- a/Doc/using/configure.rst
|
Index: Python-3.11.14/Doc/using/configure.rst
|
||||||
+++ b/Doc/using/configure.rst
|
===================================================================
|
||||||
@@ -43,7 +43,6 @@ General Options
|
--- 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 @@
|
||||||
|
|
||||||
See :data:`sys.int_info.bits_per_digit <sys.int_info>`.
|
See :data:`sys.int_info.bits_per_digit <sys.int_info>`.
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
.. option:: --with-cxx-main=COMPILER
|
.. option:: --with-cxx-main=COMPILER
|
||||||
|
|
||||||
Compile the Python ``main()`` function and link Python executable with C++
|
Compile the Python ``main()`` function and link Python executable with C++
|
||||||
@@ -529,13 +528,11 @@ macOS Options
|
@@ -529,13 +528,11 @@
|
||||||
|
|
||||||
See ``Mac/README.rst``.
|
See ``Mac/README.rst``.
|
||||||
|
|
||||||
@@ -27,9 +29,11 @@
|
|||||||
.. option:: --enable-framework=INSTALLDIR
|
.. option:: --enable-framework=INSTALLDIR
|
||||||
|
|
||||||
Create a Python.framework rather than a traditional Unix install. Optional
|
Create a Python.framework rather than a traditional Unix install. Optional
|
||||||
--- a/Misc/NEWS
|
Index: Python-3.11.14/Misc/NEWS
|
||||||
+++ b/Misc/NEWS
|
===================================================================
|
||||||
@@ -9911,7 +9911,7 @@ C API
|
--- 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 @@
|
||||||
- bpo-40939: Removed documentation for the removed ``PyParser_*`` 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
|
- bpo-43795: The list in :ref:`limited-api-list` now shows the public name
|
||||||
|
|||||||
36
gh139257-Support-docutils-0.22.patch
Normal file
36
gh139257-Support-docutils-0.22.patch
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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
|
||||||
@@ -1,3 +1,95 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
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>
|
Fri Sep 19 14:38:03 UTC 2025 - Dominique Leuenberger <dimstar@opensuse.org>
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
# _md5.cpython-38m-x86_64-linux-gnu.so
|
# _md5.cpython-38m-x86_64-linux-gnu.so
|
||||||
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
%define dynlib() %{sitedir}/lib-dynload/%{1}.cpython-%{abi_tag}-%{archname}-%{_os}%{?_gnu}%{?armsuffix}.so
|
||||||
Name: %{python_pkg_name}%{psuffix}
|
Name: %{python_pkg_name}%{psuffix}
|
||||||
Version: 3.11.13
|
Version: 3.11.14
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Python 3 Interpreter
|
Summary: Python 3 Interpreter
|
||||||
License: Python-2.0
|
License: Python-2.0
|
||||||
@@ -186,12 +186,20 @@ Patch19: bso1227999-reproducible-builds.patch
|
|||||||
Patch22: gh120226-fix-sendfile-test-kernel-610.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
|
# PATCH-FIX-UPSTREAM Add platform triplets for 64-bit LoongArch gh#python/cpython#30939 glaubitz@suse.com
|
||||||
Patch24: add-loongarch64-support.patch
|
Patch24: add-loongarch64-support.patch
|
||||||
# PATCH-FIX-UPSTREAM CVE-2025-6069-quad-complex-HTMLParser.patch bsc#1244705 mcepl@suse.com
|
# PATCH-FIX-OPENSUSE gh139257-Support-docutils-0.22.patch gh#python/cpython#139257 daniel.garcia@suse.com
|
||||||
# avoid quadratic complexity when processing malformed inputs with HTMLParser
|
Patch25: gh139257-Support-docutils-0.22.patch
|
||||||
Patch25: CVE-2025-6069-quad-complex-HTMLParser.patch
|
# PATCH-FIX-UPSTREAM CVE-2025-6075-expandvars-perf-degrad.patch bsc#1252974 mcepl@suse.com
|
||||||
# PATCH-FIX-UPSTREAM CVE-2025-8194-tarfile-no-neg-offsets.patch bsc#1247249 mcepl@suse.com
|
# Avoid potential quadratic complexity vulnerabilities in path modules
|
||||||
# tarfile now validates archives to ensure member offsets are non-negative
|
Patch26: CVE-2025-6075-expandvars-perf-degrad.patch
|
||||||
Patch26: CVE-2025-8194-tarfile-no-neg-offsets.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
|
||||||
BuildRequires: autoconf-archive
|
BuildRequires: autoconf-archive
|
||||||
BuildRequires: automake
|
BuildRequires: automake
|
||||||
BuildRequires: crypto-policies-scripts
|
BuildRequires: crypto-policies-scripts
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
Lib/test/support/__init__.py | 2 +-
|
Lib/test/support/__init__.py | 2 +-
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
--- a/Lib/test/support/__init__.py
|
Index: Python-3.11.14/Lib/test/support/__init__.py
|
||||||
+++ b/Lib/test/support/__init__.py
|
===================================================================
|
||||||
@@ -384,7 +384,7 @@ def skip_if_buildbot(reason=None):
|
--- 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 @@
|
||||||
if not reason:
|
if not reason:
|
||||||
reason = 'not suitable for buildbots'
|
reason = 'not suitable for buildbots'
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user