https://github.com/python/cpython/commit/54717b652dc28b9a3c2ccb49b6c0b1fea5a39655 commit: 54717b652dc28b9a3c2ccb49b6c0b1fea5a39655 branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-09-02T21:41:55Z summary:
[3.14] gh-156797: Escape the namespace URI in xml.sax.saxutils.XMLGenerator (GH-156799) (GH-156857) 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 <[email protected]> files: A Misc/NEWS.d/next/Library/2026-09-01-16-58-30.gh-issue-156797.Qm4tRs.rst M Lib/test/test_sax.py M Lib/xml/sax/saxutils.py diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 5c10bcedc69bc6..e88e9b65b4fc6c 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -614,6 +614,22 @@ def test_xmlgen_ns(self): '<ns1:doc xmlns:ns1="%s"><udoc></udoc></ns1:doc>' % 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( + """<ns1:doc xmlns:ns1='http://example.org/?a="1"&b=<2>'>""" + "</ns1:doc>")) + 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 77ab9ee2faf038..920283c3f3496f 100644 --- a/Lib/xml/sax/saxutils.py +++ b/Lib/xml/sax/saxutils.py @@ -187,9 +187,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 00000000000000..7879ac7e71c71d --- /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. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
