diff --git a/CVE-2023-52425-libexpat-2.6.0-backport.patch b/CVE-2023-52425-libexpat-2.6.0-backport.patch new file mode 100644 index 0000000..f733b09 --- /dev/null +++ b/CVE-2023-52425-libexpat-2.6.0-backport.patch @@ -0,0 +1,313 @@ +From d7133c7e0f91b14c390aa30a5689c353ef754fb6 Mon Sep 17 00:00:00 2001 +From: Sebastian Pipping +Date: Wed, 7 Feb 2024 15:32:45 +0100 +Subject: [PATCH] Fix etree XMLPullParser tests for Expat >=2.6.0 with reparse + deferral + +Combined with gh#python/cpython!31453 +bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453) + +Curly brackets were never allowed in namespace URIs +according to RFC 3986, and so-called namespace-validating +XML parsers have the right to reject them a invalid URIs. + +libexpat >=2.4.5 has become strcter in that regard due to +related security issues; with ET.XML instantiating a +namespace-aware parser under the hood, this test has no +future in CPython. + +References: +- https://datatracker.ietf.org/doc/html/rfc3968 +- https://www.w3.org/TR/xml-names/ + +Also, test_minidom.py: Support Expat >=2.4.5 +(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e) + +Co-authored-by: Sebastian Pipping +Fixes: gh#python/cpython#115133 +From-PR: gh#python/cpython!115138 +Patch: CVE-2023-52425-libexpat-2.6.0-backport.patch +--- + Lib/test/support/__init__.py | 15 ++ + Lib/test/test_minidom.py | 22 +-- + Lib/test/test_pyexpat.py | 61 +++++++++- + Lib/test/test_sax.py | 54 ++++++++ + Lib/test/test_xml_etree.py | 13 +- + Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst | 1 + Misc/NEWS.d/next/Tests/2024-02-07-15-49-37.gh-issue-115133.WBajNr.rst | 1 + 7 files changed, 146 insertions(+), 21 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst + create mode 100644 Misc/NEWS.d/next/Tests/2024-02-07-15-49-37.gh-issue-115133.WBajNr.rst + +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -23,6 +23,7 @@ import platform + import re + import shutil + import socket ++import pyexpat + import stat + import struct + import subprocess +@@ -119,9 +120,11 @@ __all__ = [ + "run_with_locale", "swap_item", + "swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict", + "run_with_tz", "PGO", "missing_compiler_executable", "fd_count", +- "ALWAYS_EQ", "LARGEST", "SMALLEST" ++ "ALWAYS_EQ", "LARGEST", "SMALLEST", ++ "fails_with_expat_2_6_0", "is_expat_2_6_0" + ] + ++ + class Error(Exception): + """Base class for regression test exceptions.""" + +@@ -3411,3 +3414,13 @@ def adjust_int_max_str_digits(max_digits + yield + finally: + sys.set_int_max_str_digits(current) ++ ++ ++@functools.lru_cache(maxsize=32) ++def _is_expat_2_6_0(): ++ return hasattr(pyexpat.ParserCreate(), 'SetReparseDeferralEnabled') ++is_expat_2_6_0 = _is_expat_2_6_0() ++ ++fails_with_expat_2_6_0 = (unittest.expectedFailure ++ if is_expat_2_6_0 ++ else lambda test: test) +--- a/Lib/test/test_minidom.py ++++ b/Lib/test/test_minidom.py +@@ -1149,13 +1149,11 @@ class MinidomTest(unittest.TestCase): + + # Verify that character decoding errors raise exceptions instead + # of crashing +- if pyexpat.version_info >= (2, 4, 5): +- self.assertRaises(ExpatError, parseString, +- b'') +- self.assertRaises(ExpatError, parseString, +- b'Comment \xe7a va ? Tr\xe8s bien ?') +- else: +- self.assertRaises(UnicodeDecodeError, parseString, ++ # It doesn’t make any sense to insist on the exact text of the ++ # error message, or even the exact Exception … it is enough that ++ # the error has been discovered. ++ with self.assertRaises((UnicodeDecodeError, ExpatError)): ++ parseString( + b'Comment \xe7a va ? Tr\xe8s bien ?') + + doc.unlink() +@@ -1601,12 +1599,10 @@ class MinidomTest(unittest.TestCase): + self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) + + def testExceptionOnSpacesInXMLNSValue(self): +- if pyexpat.version_info >= (2, 4, 5): +- context = self.assertRaisesRegex(ExpatError, 'syntax error') +- else: +- context = self.assertRaisesRegex(ValueError, 'Unsupported syntax') +- +- with context: ++ # It doesn’t make any sense to insist on the exact text of the ++ # error message, or even the exact Exception … it is enough that ++ # the error has been discovered. ++ with self.assertRaises((ExpatError, ValueError)): + parseString('') + + def testDocRemoveChild(self): +--- a/Lib/test/test_pyexpat.py ++++ b/Lib/test/test_pyexpat.py +@@ -11,7 +11,7 @@ import traceback + from xml.parsers import expat + from xml.parsers.expat import errors + +-from test.support import sortdict ++from test.support import sortdict, is_expat_2_6_0 + + + class SetAttributeTest(unittest.TestCase): +@@ -778,6 +778,65 @@ class ReparseDeferralTest(unittest.TestC + + for chunk in (b''): + parser.Parse(chunk, False) ++ ++ # The key test: Have handlers already fired? Expecting: yes. ++ self.assertEqual(started, ['doc']) ++ ++ ++class ReparseDeferralTest(unittest.TestCase): ++ def test_getter_setter_round_trip(self): ++ if not is_expat_2_6_0: ++ self.skipTest("Linked libexpat doesn't support reparse deferral") ++ ++ parser = expat.ParserCreate() ++ enabled = (expat.version_info >= (2, 6, 0)) ++ ++ self.assertIs(parser.GetReparseDeferralEnabled(), enabled) ++ parser.SetReparseDeferralEnabled(False) ++ self.assertIs(parser.GetReparseDeferralEnabled(), False) ++ parser.SetReparseDeferralEnabled(True) ++ self.assertIs(parser.GetReparseDeferralEnabled(), enabled) ++ ++ def test_reparse_deferral_enabled(self): ++ if not is_expat_2_6_0: ++ self.skipTest("Linked libexpat doesn't support reparse deferral") ++ ++ started = [] ++ ++ def start_element(name, _): ++ started.append(name) ++ ++ parser = expat.ParserCreate() ++ parser.StartElementHandler = start_element ++ self.assertTrue(parser.GetReparseDeferralEnabled()) ++ ++ for chunk in (b''): ++ parser.Parse(chunk, False) ++ ++ # The key test: Have handlers already fired? Expecting: no. ++ self.assertEqual(started, []) ++ ++ parser.Parse(b'', True) ++ ++ self.assertEqual(started, ['doc']) ++ ++ def test_reparse_deferral_disabled(self): ++ if not is_expat_2_6_0: ++ self.skipTest("Linked libexpat doesn't support reparse deferral") ++ ++ started = [] ++ ++ def start_element(name, _): ++ started.append(name) ++ ++ parser = expat.ParserCreate() ++ parser.StartElementHandler = start_element ++ if is_expat_2_6_0: ++ parser.SetReparseDeferralEnabled(False) ++ self.assertFalse(parser.GetReparseDeferralEnabled()) ++ ++ for chunk in (b''): ++ parser.Parse(chunk, False) + + # The key test: Have handlers already fired? Expecting: yes. + self.assertEqual(started, ['doc']) +--- a/Lib/test/test_sax.py ++++ b/Lib/test/test_sax.py +@@ -22,7 +22,7 @@ import pyexpat + import shutil + from urllib.error import URLError + from test import support +-from test.support import findfile, run_unittest, FakePath, TESTFN ++from test.support import findfile, run_unittest, FakePath, TESTFN, is_expat_2_6_0 + + TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") + TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") +@@ -1247,6 +1247,58 @@ class ExpatReaderTest(XmlTestBase): + + self.assertFalse(parser._parser.GetReparseDeferralEnabled()) + ++ parser.flush() ++ ++ self.assertFalse(parser._parser.GetReparseDeferralEnabled()) ++ self.assertEqual(result.getvalue(), start + b"") ++ ++ parser.feed("") ++ parser.close() ++ ++ self.assertEqual(result.getvalue(), start + b"") ++ ++ def test_flush_reparse_deferral_enabled(self): ++ if not is_expat_2_6_0: ++ self.skipTest("Linked libexpat doesn't support reparse deferral") ++ ++ result = BytesIO() ++ xmlgen = XMLGenerator(result) ++ parser = create_parser() ++ parser.setContentHandler(xmlgen) ++ ++ for chunk in (""): ++ parser.feed(chunk) ++ ++ self.assertEqual(result.getvalue(), start) # i.e. no elements started ++ self.assertTrue(parser._parser.GetReparseDeferralEnabled()) ++ ++ parser.flush() ++ ++ self.assertTrue(parser._parser.GetReparseDeferralEnabled()) ++ self.assertEqual(result.getvalue(), start + b"") ++ ++ parser.feed("") ++ parser.close() ++ ++ self.assertEqual(result.getvalue(), start + b"") ++ ++ def test_flush_reparse_deferral_disabled(self): ++ if not is_expat_2_6_0: ++ self.skipTest("Linked libexpat doesn't support reparse deferral") ++ ++ result = BytesIO() ++ xmlgen = XMLGenerator(result) ++ parser = create_parser() ++ parser.setContentHandler(xmlgen) ++ ++ for chunk in (""): ++ parser.feed(chunk) ++ ++ parser._parser.SetReparseDeferralEnabled(False) ++ self.assertEqual(result.getvalue(), start) # i.e. no elements started ++ ++ self.assertFalse(parser._parser.GetReparseDeferralEnabled()) ++ + parser.flush() + + self.assertFalse(parser._parser.GetReparseDeferralEnabled()) +--- a/Lib/test/test_xml_etree.py ++++ b/Lib/test/test_xml_etree.py +@@ -25,7 +25,8 @@ import weakref + from functools import partial + from itertools import product, islice + from test import support +-from test.support import TESTFN, findfile, import_fresh_module, gc_collect, swap_attr ++from test.support import (TESTFN, findfile, import_fresh_module, ++ gc_collect, swap_attr, is_expat_2_6_0, fails_with_expat_2_6_0) + + # pyET is the pure-Python implementation. + # +@@ -1271,6 +1272,7 @@ class XMLPullParserTest(unittest.TestCas + expected) + + def test_simple_xml(self, chunk_size=None, flush=False): ++ expected_events = [] + parser = ET.XMLPullParser() + self.assert_event_tags(parser, []) + self._feed(parser, "\n", chunk_size, flush) +@@ -1280,16 +1282,17 @@ class XMLPullParserTest(unittest.TestCas + chunk_size, flush) + self.assert_event_tags(parser, []) + self._feed(parser, ">\n", chunk_size, flush) +- self.assert_event_tags(parser, [('end', 'element')]) ++ expected_events += [('end', 'element')] + self._feed(parser, "texttail\n", chunk_size, flush) + self._feed(parser, "\n", chunk_size, flush) +- self.assert_event_tags(parser, [ ++ expected_events += [ + ('end', 'element'), + ('end', 'empty-element'), +- ]) ++ ] + self._feed(parser, "\n", chunk_size, flush) +- self.assert_event_tags(parser, [('end', 'root')]) ++ expected_events += [('end', 'root')] + self.assertIsNone(parser.close()) ++ self.assert_event_tags(parser, expected_events) + + def test_simple_xml_chunk_1(self): + self.test_simple_xml(chunk_size=1, flush=True) +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst +@@ -0,0 +1 @@ ++Make test suite support Expat >=2.4.5 +--- /dev/null ++++ b/Misc/NEWS.d/next/Tests/2024-02-07-15-49-37.gh-issue-115133.WBajNr.rst +@@ -0,0 +1 @@ ++Fix etree XMLPullParser tests for Expat >=2.6.0 with reparse deferral diff --git a/python38.changes b/python38.changes index c79b3bd..dd272fd 100644 --- a/python38.changes +++ b/python38.changes @@ -1,3 +1,17 @@ +------------------------------------------------------------------- +Fri Sep 20 22:19:12 UTC 2024 - Matej Cepl + +- Add sphinx-802.patch to overcome working both with the most + recent and older Sphinx versions. + +------------------------------------------------------------------- +Thu Sep 19 00:14:25 UTC 2024 - Matej Cepl + +- Update CVE-2023-52425-libexpat-2.6.0-backport.patch + so that it uses features sniffing, not just + comparing version number. Include also + support-expat-CVE-2022-25236-patched.patch. + ------------------------------------------------------------------- Mon Sep 9 20:27:46 UTC 2024 - Matej Cepl diff --git a/python38.spec b/python38.spec index 387efd4..98988a5 100644 --- a/python38.spec +++ b/python38.spec @@ -174,9 +174,11 @@ Patch33: bpo44426-complex-keyword-sphinx.patch # PATCH-FIX-UPSTREAM bpo34990-2038-problem-compileall.patch gh#python/cpython#79171 mcepl@suse.com # Make compileall.py compatible with year 2038 Patch34: bpo34990-2038-problem-compileall.patch -# PATCH-FIX-UPSTREAM gh#python/cpython#90967 gh#python/cpython#93900 mcepl@suse.com -# NOTE: SUSE version of expat 2.4.4 is patched in SUSE for CVE-2022-25236 -Patch36: support-expat-CVE-2022-25236-patched.patch +# PATCH-FIX-OPENSUSE CVE-2023-52425-libexpat-2.6.0-backport.patch +# This problem on libexpat is patched on SLE without version +# update, this patch changes the tests to match the libexpat provided +# by SUSE +Patch36: CVE-2023-52425-libexpat-2.6.0-backport.patch # PATCH-FIX-OPENSUSE platlibdir-in-sys.patch bsc#1204395 Patch37: platlibdir-in-sys.patch # PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com @@ -194,6 +196,9 @@ Patch48: CVE-2024-5642-OpenSSL-API-buf-overread-NPN.patch # PATCH-FIX-UPSTREAM gh120226-fix-sendfile-test-kernel-610.patch gh#python/cpython#120226 mcepl@suse.com # Fix test_sendfile_close_peer_in_the_middle_of_receiving on Linux >= 6.10 (GH-120227) Patch50: gh120226-fix-sendfile-test-kernel-610.patch +# PATCH-FIX-UPSTREAM sphinx-802.patch mcepl@suse.com +# status_iterator method moved between the Sphinx versions +Patch51: sphinx-802.patch BuildRequires: autoconf-archive BuildRequires: automake @@ -468,6 +473,7 @@ other applications. %patch -p1 -P 46 %patch -p1 -P 48 %patch -p1 -P 50 +%patch -p1 -P 51 # drop Autoconf version requirement sed -i 's/^AC_PREREQ/dnl AC_PREREQ/' configure.ac diff --git a/sphinx-802.patch b/sphinx-802.patch new file mode 100644 index 0000000..c4600b7 --- /dev/null +++ b/sphinx-802.patch @@ -0,0 +1,21 @@ +--- + Doc/tools/extensions/pyspecific.py | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/Doc/tools/extensions/pyspecific.py ++++ b/Doc/tools/extensions/pyspecific.py +@@ -27,7 +27,13 @@ try: + except ImportError: + from sphinx.environment import NoUri + from sphinx.locale import _ as sphinx_gettext +-from sphinx.util import status_iterator, logging ++try: ++ from sphinx.util.display import status_iterator ++except ImportError: ++ # This method was moved into sphinx.util.display in Sphinx 6.1.0. Before ++ # that it resided in sphinx.util. ++ from sphinx.util import status_iterator ++from sphinx.util import logging + from sphinx.util.nodes import split_explicit_title + from sphinx.writers.text import TextWriter, TextTranslator + from sphinx.writers.latex import LaTeXTranslator diff --git a/support-expat-CVE-2022-25236-patched.patch b/support-expat-CVE-2022-25236-patched.patch deleted file mode 100644 index c225a8f..0000000 --- a/support-expat-CVE-2022-25236-patched.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 7da97f61816f3cadaa6788804b22a2434b40e8c5 Mon Sep 17 00:00:00 2001 -From: "Miss Islington (bot)" - <31488909+miss-islington@users.noreply.github.com> -Date: Mon, 21 Feb 2022 08:16:09 -0800 -Subject: [PATCH] bpo-46811: Make test suite support Expat >=2.4.5 (GH-31453) - (GH-31472) - -Curly brackets were never allowed in namespace URIs -according to RFC 3986, and so-called namespace-validating -XML parsers have the right to reject them a invalid URIs. - -libexpat >=2.4.5 has become strcter in that regard due to -related security issues; with ET.XML instantiating a -namespace-aware parser under the hood, this test has no -future in CPython. - -References: -- https://datatracker.ietf.org/doc/html/rfc3968 -- https://www.w3.org/TR/xml-names/ - -Also, test_minidom.py: Support Expat >=2.4.5 -(cherry picked from commit 2cae93832f46b245847bdc252456ddf7742ef45e) - -Co-authored-by: Sebastian Pipping ---- - Lib/test/test_minidom.py | 25 +++++++++++-------------- - 1 file changed, 11 insertions(+), 14 deletions(-) - create mode 100644 Misc/NEWS.d/next/Library/2022-02-20-21-03-31.bpo-46811.8BxgdQ.rst - ---- a/Lib/test/test_minidom.py -+++ b/Lib/test/test_minidom.py -@@ -1149,14 +1149,12 @@ class MinidomTest(unittest.TestCase): - - # Verify that character decoding errors raise exceptions instead - # of crashing -- if pyexpat.version_info >= (2, 4, 5): -- self.assertRaises(ExpatError, parseString, -- b'') -- self.assertRaises(ExpatError, parseString, -- b'Comment \xe7a va ? Tr\xe8s bien ?') -- else: -- self.assertRaises(UnicodeDecodeError, parseString, -- b'Comment \xe7a va ? Tr\xe8s bien ?') -+ # It doesn’t make any sense to insist on the exact text of the -+ # error message, or even the exact Exception … it is enough that -+ # the error has been discovered. -+ with self.assertRaises((UnicodeDecodeError, ExpatError)): -+ parseString( -+ b'Comment \xe7a va ? Tr\xe8s bien ?') - - doc.unlink() - -@@ -1601,13 +1599,12 @@ class MinidomTest(unittest.TestCase): - self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) - - def testExceptionOnSpacesInXMLNSValue(self): -- if pyexpat.version_info >= (2, 4, 5): -- context = self.assertRaisesRegex(ExpatError, 'syntax error') -- else: -- context = self.assertRaisesRegex(ValueError, 'Unsupported syntax') -+ # It doesn’t make any sense to insist on the exact text of the -+ # error message, or even the exact Exception … it is enough that -+ # the error has been discovered. -+ with self.assertRaises((ExpatError, ValueError)): -+ parseString('') - -- with context: -- parseString('') - - def testDocRemoveChild(self): - doc = parse(tstfile)