From 8f2594e5976b2ac82bf399e4807f2e60211c515e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 3 Sep 2026 00:10:23 +0300 Subject: [PATCH] gh-156797: Escape the namespace URI in xml.sax.saxutils.XMLGenerator (GH-156799) The namespace declarations were the only attribute values written without quoteattr(), so a URI containing "&", "<" or a quote character produced a document which cannot be parsed. (cherry picked from commit 5056ac552a413b394fd189ac2c8b8a519a1c9580) Co-authored-by: Serhiy Storchaka --- Lib/test/test_sax.py | 16 ++++++++++++++++ Lib/xml/sax/saxutils.py | 4 ++-- ...026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst | 3 +++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 29babd7bf6996ad..46b29650d9383b5 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -614,6 +614,22 @@ def test_xmlgen_ns(self): '' % ns_uri)) + def test_xmlgen_ns_escaped_uri(self): + uri = 'http://example.org/?a="1"&b=<2>' + result = self.ioclass() + gen = XMLGenerator(result) + + gen.startDocument() + gen.startPrefixMapping("ns1", uri) + gen.startElementNS((uri, "doc"), "ns1:doc", {}) + gen.endElementNS((uri, "doc"), "ns1:doc") + gen.endPrefixMapping("ns1") + gen.endDocument() + + self.assertEqual(result.getvalue(), self.xml( + """""" + "")) + def test_xmlgen_ns_empty(self): result = self.ioclass() gen = XMLGenerator(result, short_empty_elements=True) diff --git a/Lib/xml/sax/saxutils.py b/Lib/xml/sax/saxutils.py index c1612ea1cebc5d0..637269eb9c8a4de 100644 --- a/Lib/xml/sax/saxutils.py +++ b/Lib/xml/sax/saxutils.py @@ -186,9 +186,9 @@ def startElementNS(self, name, qname, attrs): for prefix, uri in self._undeclared_ns_maps: if prefix: - self._write(' xmlns:%s="%s"' % (prefix, uri)) + self._write(' xmlns:%s=%s' % (prefix, quoteattr(uri))) else: - self._write(' xmlns="%s"' % uri) + self._write(' xmlns=%s' % quoteattr(uri)) self._undeclared_ns_maps = [] for (name, value) in attrs.items(): diff --git a/Misc/NEWS.d/next/Library/2026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst b/Misc/NEWS.d/next/Library/2026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst new file mode 100644 index 000000000000000..7879ac7e71c71d9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst @@ -0,0 +1,3 @@ +Fix :class:`xml.sax.saxutils.XMLGenerator`: the namespace URI is now escaped +in the namespace declaration. Previously a URI containing ``&``, ``<`` or a +quote character produced a document which cannot be parsed.