Sync from SUSE:ALP:Source:Standard:1.0 python311 revision 1c887b70c7de280aee269b6039e351c7

This commit is contained in:
2025-12-09 07:48:24 +01:00
parent e3c3203a7c
commit a306375b7d
15 changed files with 581 additions and 471 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.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.12/Lib/test/support/__init__.py 2025-04-11 10:52:44.802161741 +0200
--- 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
@@ -8,6 +8,7 @@
import functools
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
@@ -46,10 +46,10 @@ Index: Python-3.11.12/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.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.12/Lib/test/test_minidom.py 2025-04-11 10:52:44.802522893 +0200
--- Python-3.11.14.orig/Lib/test/test_minidom.py 2025-11-15 19:14:53.915952608 +0100
+++ Python-3.11.14/Lib/test/test_minidom.py 2025-11-15 19:15:12.859877278 +0100
@@ -6,7 +6,6 @@
from test import support
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>')
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.12/Lib/test/test_pyexpat.py 2025-04-11 10:52:44.803228085 +0200
--- Python-3.11.14.orig/Lib/test/test_pyexpat.py 2025-11-15 19:14:53.915952608 +0100
+++ Python-3.11.14/Lib/test/test_pyexpat.py 2025-11-15 19:15:12.860334045 +0100
@@ -14,8 +14,7 @@
from xml.parsers import expat
from xml.parsers.expat import errors
@@ -108,7 +108,7 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
class SetAttributeTest(unittest.TestCase):
def setUp(self):
@@ -770,9 +769,8 @@
@@ -806,9 +805,8 @@
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
def test_reparse_deferral_enabled(self):
@@ -120,7 +120,7 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
started = []
@@ -801,9 +799,9 @@
@@ -837,9 +835,9 @@
parser = expat.ParserCreate()
parser.StartElementHandler = start_element
@@ -132,10 +132,10 @@ Index: Python-3.11.12/Lib/test/test_pyexpat.py
for chunk in (b'<doc', b'/>'):
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.12/Lib/test/test_sax.py 2025-04-11 10:52:44.803567098 +0200
--- 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
@@ -19,13 +19,11 @@
from io import BytesIO, StringIO
import codecs
@@ -187,10 +187,10 @@ Index: Python-3.11.12/Lib/test/test_sax.py
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.12/Lib/test/test_xml_etree.py 2025-04-11 10:52:44.804234785 +0200
--- 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
@@ -13,7 +13,6 @@
import operator
import os

View File

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

View File

@@ -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.

View 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`.

View File

@@ -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`.)

Binary file not shown.

View File

@@ -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

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -3,9 +3,11 @@
Misc/NEWS | 2 +-
2 files changed, 1 insertion(+), 4 deletions(-)
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -43,7 +43,6 @@ General Options
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 @@
See :data:`sys.int_info.bits_per_digit <sys.int_info>`.
@@ -13,7 +15,7 @@
.. option:: --with-cxx-main=COMPILER
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``.
@@ -27,9 +29,11 @@
.. option:: --enable-framework=INSTALLDIR
Create a Python.framework rather than a traditional Unix install. Optional
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -9911,7 +9911,7 @@ C API
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 @@
- 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

@@ -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

View File

@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2017 Zbigniew Jędrzejewski-Szmek -->
<application>
<id type="desktop">idle3.desktop</id>
<component type="desktop-application">
<id>org.python.IDLE3</id>
<launchable type="desktop-id">idle3.desktop</launchable>
<name>IDLE3</name>
<metadata_licence>CC0</metadata_licence>
<project_license>Python-2.0</project_license>
<summary>Python 3 Integrated Development and Learning Environment</summary>
<description>
<p>
IDLE is Pythons Integrated Development and Learning Environment.
The GUI is uniform between Windows, Unix, and Mac OS X.
The GUI is uniform between Windows, Unix, and macOS.
IDLE provides an easy way to start writing, running, and debugging
Python code.
</p>
@@ -19,17 +19,33 @@
It provides:
</p>
<ul>
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
<li>a Python shell window (interactive interpreter) with colorizing of code input, output, and error messages,</li>
<li>a multi-window text editor with multiple undo, Python colorizing, smart indent, call tips, auto completion, and other features,</li>
<li>search within any window, replace within editor windows, and search through multiple files (grep),</li>
<li>a debugger with persistent breakpoints, stepping, and viewing of global and local namespaces.</li>
</ul>
</description>
<developer id="org.python">
<name>Python Software Foundation</name>
</developer>
<url type="homepage">https://docs.python.org/3/library/idle.html</url>
<screenshots>
<screenshot type="default">http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</screenshot>
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</screenshot>
<screenshot>http://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</screenshot>
<screenshot type="default">
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-main-window.png</image>
</screenshot>
<screenshot>
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-class-browser.png</image>
</screenshot>
<screenshot>
<image>https://in.waw.pl/~zbyszek/fedora/idle3-appdata/idle3-code-viewer.png</image>
</screenshot>
</screenshots>
<project_license>Python-2.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
<update_contact>zbyszek@in.waw.pl</update_contact>
</application>
</component>

View File

@@ -1,3 +1,96 @@
-------------------------------------------------------------------
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>
- Drop AppStream buildrequires and don't run appstreamcli validate
as part of the build process: the appdata.xml is not updated by
source directly, so we have more contol. Having Appstream or the
deprecated appstream-glib result in a build cycle.
-------------------------------------------------------------------
Thu Sep 18 08:15:31 UTC 2025 - Dominique Leuenberger <dimstar@opensuse.org>
- Require AppStream to validate appdata file instead of deprecated
appstream-glib.
- Update idle3.appdata.xml to pass the more pedantic appstreamcli.
-------------------------------------------------------------------
Fri Aug 1 20:09:24 UTC 2025 - Matej Cepl <mcepl@cepl.eu>

View File

@@ -1,7 +1,7 @@
#
# spec file for package python311
#
# Copyright (c) 2025 SUSE LLC
# Copyright (c) 2025 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -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.13
Version: 3.11.14
Release: 0
Summary: Python 3 Interpreter
License: Python-2.0
@@ -186,12 +186,11 @@ 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-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
# 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
BuildRequires: autoconf-archive
BuildRequires: automake
BuildRequires: crypto-policies-scripts
@@ -226,8 +225,6 @@ BuildRequires: python3-python-docs-theme >= 2022.1
%endif
%endif
%if %{with general}
# required for idle3 (.desktop and .appdata.xml files)
BuildRequires: appstream-glib
BuildRequires: gcc-c++
BuildRequires: gdbm-devel
BuildRequires: gettext
@@ -685,7 +682,6 @@ install -m 644 -D -t %{buildroot}%{_datadir}/applications idle%{python_version}.
cp %{SOURCE20} idle%{python_version}.appdata.xml
sed -i -e 's:idle3.desktop:idle%{python_version}.desktop:g' idle%{python_version}.appdata.xml
install -m 644 -D -t %{buildroot}%{_datadir}/metainfo idle%{python_version}.appdata.xml
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/idle%{python_version}.appdata.xml
%fdupes %{buildroot}/%{_libdir}/python%{python_version}
%endif

View File

@@ -2,9 +2,11 @@
Lib/test/support/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -384,7 +384,7 @@ def skip_if_buildbot(reason=None):
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 @@
if not reason:
reason = 'not suitable for buildbots'
try: