Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-xmltodict for
openSUSE:Factory checked in at 2025-09-10 17:30:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-xmltodict (Old)
and /work/SRC/openSUSE:Factory/.python-xmltodict.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-xmltodict"
Wed Sep 10 17:30:07 2025 rev:11 rq:1303377 version:0.15.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-xmltodict/python-xmltodict.changes
2024-10-30 17:32:53.432372473 +0100
+++
/work/SRC/openSUSE:Factory/.python-xmltodict.new.1977/python-xmltodict.changes
2025-09-10 17:30:10.141349759 +0200
@@ -1,0 +2,21 @@
+Tue Sep 9 07:46:33 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to version 0.15.1
+ * Security: Further harden XML injection prevention during unparse (follow-up
+ to v0.15.0). In addition to '<'/'>' rejection, now also reject element and
+ attribute names (including `@xmlns` prefixes) that:
+ - start with '?' or '!'
+ - contain '/' or any whitespace
+ - contain quotes (' or ") or '='
+ - are non-strings (names must be `str`; no coercion)
+
+-------------------------------------------------------------------
+Mon Sep 8 11:26:39 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to version 0.15.0
+ * Security: Prevent XML injection (CVE-2025-9375) by rejecting '<'/'>' in
+ element and attribute names (including `@xmlns` prefixes) during unparse.
+ This limits validation to avoiding tag-context escapes; attribute values
+ continue to be escaped by the SAX `XMLGenerator`. (bsc#1249036,
CVE-2025-9375)
+
+-------------------------------------------------------------------
Old:
----
xmltodict-0.14.2.tar.gz
New:
----
xmltodict-0.15.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-xmltodict.spec ++++++
--- /var/tmp/diff_new_pack.IIMlv8/_old 2025-09-10 17:30:11.013386428 +0200
+++ /var/tmp/diff_new_pack.IIMlv8/_new 2025-09-10 17:30:11.013386428 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-xmltodict
-Version: 0.14.2
+Version: 0.15.1
Release: 0
Summary: Module to make XML working resemble JSON
License: MIT
++++++ xmltodict-0.14.2.tar.gz -> xmltodict-0.15.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/PKG-INFO
new/xmltodict-0.15.1/PKG-INFO
--- old/xmltodict-0.14.2/PKG-INFO 2024-10-16 08:10:02.723137400 +0200
+++ new/xmltodict-0.15.1/PKG-INFO 2025-09-08 20:33:12.287541400 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: xmltodict
-Version: 0.14.2
+Version: 0.15.1
Summary: Makes working with XML feel like you are working with JSON
Home-page: https://github.com/martinblech/xmltodict
Author: Martin Blech
@@ -12,9 +12,6 @@
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
@@ -22,9 +19,20 @@
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Text Processing :: Markup :: XML
-Requires-Python: >=3.6
+Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: platform
+Dynamic: requires-python
+Dynamic: summary
# xmltodict
@@ -46,15 +54,15 @@
... """), indent=4))
{
"mydocument": {
- "@has": "an attribute",
+ "@has": "an attribute",
"and": {
"many": [
- "elements",
+ "elements",
"more elements"
]
- },
+ },
"plus": {
- "@a": "complex",
+ "@a": "complex",
"#text": "element as well"
}
}
@@ -110,7 +118,7 @@
>>> def handle_artist(_, artist):
... print(artist['name'])
... return True
->>>
+>>>
>>> xmltodict.parse(GzipFile('discogs_artists.xml.gz'),
... item_depth=2, item_callback=handle_artist)
A Perfect Circle
@@ -178,7 +186,7 @@
```python
>>> import xmltodict
->>>
+>>>
>>> mydict = {
... 'text': {
... '@color':'red',
@@ -234,7 +242,7 @@
### Using conda
-For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
+For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
[conda-forge channel][#xmltodict-conda] all you need to do is:
[#xmltodict-conda]: https://anaconda.org/conda-forge/xmltodict
@@ -286,3 +294,13 @@
# Python3
$ zypper in python3-xmltodict
```
+
+## Security Notes
+
+A CVE (CVE-2025-9375) was filed against `xmltodict` but is
[disputed](https://github.com/martinblech/xmltodict/issues/377#issuecomment-3255691923).
The root issue lies in Python’s `xml.sax.saxutils.XMLGenerator` API, which
does not validate XML element names and provides no built-in way to do so.
Since `xmltodict` is a thin wrapper that passes keys directly to
`XMLGenerator`, the same issue exists in the standard library itself.
+
+It has been suggested that `xml.sax.saxutils.escape()` represents a secure
usage path. This is incorrect: `escape()` is intended only for character data
and attribute values, and can produce invalid XML when misapplied to element
names. There is currently no secure, documented way in Python’s standard
library to validate XML element names.
+
+Despite this, Fluid Attacks chose to assign a CVE to `xmltodict` while leaving
the identical behavior in Python’s own standard library unaddressed. Their
disclosure process also gave only 10 days from first contact to
publication—well short of the 90-day industry norm—leaving no real opportunity
for maintainer response. These actions reflect an inconsistency of standards
and priorities that raise concerns about motivations, as they do not primarily
serve the security of the broader community.
+
+The maintainer considers this CVE invalid and will formally dispute it with
MITRE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/README.md
new/xmltodict-0.15.1/README.md
--- old/xmltodict-0.14.2/README.md 2024-10-08 19:41:22.000000000 +0200
+++ new/xmltodict-0.15.1/README.md 2025-09-04 23:33:22.000000000 +0200
@@ -18,15 +18,15 @@
... """), indent=4))
{
"mydocument": {
- "@has": "an attribute",
+ "@has": "an attribute",
"and": {
"many": [
- "elements",
+ "elements",
"more elements"
]
- },
+ },
"plus": {
- "@a": "complex",
+ "@a": "complex",
"#text": "element as well"
}
}
@@ -82,7 +82,7 @@
>>> def handle_artist(_, artist):
... print(artist['name'])
... return True
->>>
+>>>
>>> xmltodict.parse(GzipFile('discogs_artists.xml.gz'),
... item_depth=2, item_callback=handle_artist)
A Perfect Circle
@@ -150,7 +150,7 @@
```python
>>> import xmltodict
->>>
+>>>
>>> mydict = {
... 'text': {
... '@color':'red',
@@ -206,7 +206,7 @@
### Using conda
-For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
+For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
[conda-forge channel][#xmltodict-conda] all you need to do is:
[#xmltodict-conda]: https://anaconda.org/conda-forge/xmltodict
@@ -258,3 +258,13 @@
# Python3
$ zypper in python3-xmltodict
```
+
+## Security Notes
+
+A CVE (CVE-2025-9375) was filed against `xmltodict` but is
[disputed](https://github.com/martinblech/xmltodict/issues/377#issuecomment-3255691923).
The root issue lies in Python’s `xml.sax.saxutils.XMLGenerator` API, which
does not validate XML element names and provides no built-in way to do so.
Since `xmltodict` is a thin wrapper that passes keys directly to
`XMLGenerator`, the same issue exists in the standard library itself.
+
+It has been suggested that `xml.sax.saxutils.escape()` represents a secure
usage path. This is incorrect: `escape()` is intended only for character data
and attribute values, and can produce invalid XML when misapplied to element
names. There is currently no secure, documented way in Python’s standard
library to validate XML element names.
+
+Despite this, Fluid Attacks chose to assign a CVE to `xmltodict` while leaving
the identical behavior in Python’s own standard library unaddressed. Their
disclosure process also gave only 10 days from first contact to
publication—well short of the 90-day industry norm—leaving no real opportunity
for maintainer response. These actions reflect an inconsistency of standards
and priorities that raise concerns about motivations, as they do not primarily
serve the security of the broader community.
+
+The maintainer considers this CVE invalid and will formally dispute it with
MITRE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/setup.py
new/xmltodict-0.15.1/setup.py
--- old/xmltodict-0.14.2/setup.py 2024-10-09 21:19:57.000000000 +0200
+++ new/xmltodict-0.15.1/setup.py 2025-09-05 03:23:12.000000000 +0200
@@ -12,34 +12,32 @@
long_description = f.read().decode('utf-8')
-setup(name='xmltodict',
- version=xmltodict.__version__,
- description=xmltodict.__doc__,
- long_description=long_description,
- long_description_content_type='text/markdown',
- author=xmltodict.__author__,
- author_email='[email protected]',
- url='https://github.com/martinblech/xmltodict',
- license=xmltodict.__license__,
- platforms=['all'],
- python_requires='>=3.6',
- classifiers=[
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
- 'Programming Language :: Python :: 3.13',
- 'Programming Language :: Python :: Implementation :: PyPy',
- 'Topic :: Text Processing :: Markup :: XML',
- ],
- py_modules=['xmltodict'],
- tests_require=['nose2', 'coverage'],
- )
+setup(
+ name="xmltodict",
+ version=xmltodict.__version__,
+ description=xmltodict.__doc__,
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ author=xmltodict.__author__,
+ author_email="[email protected]",
+ url="https://github.com/martinblech/xmltodict",
+ license=xmltodict.__license__,
+ platforms=["all"],
+ python_requires=">=3.9",
+ classifiers=[
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Text Processing :: Markup :: XML",
+ ],
+ py_modules=["xmltodict"],
+ tests_require=["nose2", "coverage"],
+)
Binary files old/xmltodict-0.14.2/tests/__pycache__/__init__.cpython-313.pyc
and new/xmltodict-0.15.1/tests/__pycache__/__init__.cpython-313.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_dicttoxml.cpython-311.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_dicttoxml.cpython-311.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_dicttoxml.cpython-312.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_dicttoxml.cpython-312.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_dicttoxml.cpython-313.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_dicttoxml.cpython-313.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_xmltodict.cpython-311.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_xmltodict.cpython-311.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_xmltodict.cpython-312.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_xmltodict.cpython-312.pyc differ
Binary files
old/xmltodict-0.14.2/tests/__pycache__/test_xmltodict.cpython-313.pyc and
new/xmltodict-0.15.1/tests/__pycache__/test_xmltodict.cpython-313.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/tests/test_dicttoxml.py
new/xmltodict-0.15.1/tests/test_dicttoxml.py
--- old/xmltodict-0.14.2/tests/test_dicttoxml.py 2024-10-08
20:03:35.000000000 +0200
+++ new/xmltodict-0.15.1/tests/test_dicttoxml.py 2025-09-08
20:28:39.000000000 +0200
@@ -231,3 +231,109 @@
expected_xml = '<?xml version="1.0" encoding="utf-8"?>\n<x>false</x>'
xml = unparse(dict(x=False))
self.assertEqual(xml, expected_xml)
+
+ def test_rejects_tag_name_with_angle_brackets(self):
+ # Minimal guard: disallow '<' or '>' to prevent breaking tag context
+ with self.assertRaises(ValueError):
+ unparse({"m><tag>content</tag": "unsafe"}, full_document=False)
+
+ def test_rejects_attribute_name_with_angle_brackets(self):
+ # Now we expect bad attribute names to be rejected
+ with self.assertRaises(ValueError):
+ unparse(
+ {"a": {"@m><tag>content</tag": "unsafe", "#text": "x"}},
+ full_document=False,
+ )
+
+ def test_rejects_malicious_xmlns_prefix(self):
+ # xmlns prefixes go under @xmlns mapping; reject angle brackets in
prefix
+ with self.assertRaises(ValueError):
+ unparse(
+ {
+ "a": {
+ "@xmlns": {"m><bad": "http://example.com/"},
+ "#text": "x",
+ }
+ },
+ full_document=False,
+ )
+
+ def test_attribute_values_with_angle_brackets_are_escaped(self):
+ # Attribute values should be escaped by XMLGenerator
+ xml = unparse({"a": {"@attr": "1<middle>2", "#text": "x"}},
full_document=False)
+ # The generated XML should contain escaped '<' and '>' within the
attribute value
+ self.assertIn('attr="1<middle>2"', xml)
+
+ def test_rejects_tag_name_starting_with_question(self):
+ with self.assertRaises(ValueError):
+ unparse({"?pi": "data"}, full_document=False)
+
+ def test_rejects_tag_name_starting_with_bang(self):
+ with self.assertRaises(ValueError):
+ unparse({"!decl": "data"}, full_document=False)
+
+ def test_rejects_attribute_name_starting_with_question(self):
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@?weird": "x"}}, full_document=False)
+
+ def test_rejects_attribute_name_starting_with_bang(self):
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@!weird": "x"}}, full_document=False)
+
+ def test_rejects_xmlns_prefix_starting_with_question_or_bang(self):
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@xmlns": {"?p": "http://e/"}}},
full_document=False)
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@xmlns": {"!p": "http://e/"}}},
full_document=False)
+
+ def test_rejects_non_string_names(self):
+ class Weird:
+ def __str__(self):
+ return "bad>name"
+
+ # Non-string element key
+ with self.assertRaises(ValueError):
+ unparse({Weird(): "x"}, full_document=False)
+ # Non-string attribute key
+ with self.assertRaises(ValueError):
+ unparse({"a": {Weird(): "x"}}, full_document=False)
+
+ def test_rejects_tag_name_with_slash(self):
+ with self.assertRaises(ValueError):
+ unparse({"bad/name": "x"}, full_document=False)
+
+ def test_rejects_tag_name_with_whitespace(self):
+ for name in ["bad name", "bad\tname", "bad\nname"]:
+ with self.assertRaises(ValueError):
+ unparse({name: "x"}, full_document=False)
+
+ def test_rejects_attribute_name_with_slash(self):
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@bad/name": "x"}}, full_document=False)
+
+ def test_rejects_attribute_name_with_whitespace(self):
+ for name in ["@bad name", "@bad\tname", "@bad\nname"]:
+ with self.assertRaises(ValueError):
+ unparse({"a": {name: "x"}}, full_document=False)
+
+ def test_rejects_xmlns_prefix_with_slash_or_whitespace(self):
+ # Slash
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@xmlns": {"bad/prefix": "http://e/"}}},
full_document=False)
+ # Whitespace
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@xmlns": {"bad prefix": "http://e/"}}},
full_document=False)
+
+ def test_rejects_names_with_quotes_and_equals(self):
+ # Element names
+ for name in ['a"b', "a'b", "a=b"]:
+ with self.assertRaises(ValueError):
+ unparse({name: "x"}, full_document=False)
+ # Attribute names
+ for name in ['@a"b', "@a'b", "@a=b"]:
+ with self.assertRaises(ValueError):
+ unparse({"a": {name: "x"}}, full_document=False)
+ # xmlns prefixes
+ for prefix in ['a"b', "a'b", "a=b"]:
+ with self.assertRaises(ValueError):
+ unparse({"a": {"@xmlns": {prefix: "http://e/"}}},
full_document=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/xmltodict.egg-info/PKG-INFO
new/xmltodict-0.15.1/xmltodict.egg-info/PKG-INFO
--- old/xmltodict-0.14.2/xmltodict.egg-info/PKG-INFO 2024-10-16
08:10:02.000000000 +0200
+++ new/xmltodict-0.15.1/xmltodict.egg-info/PKG-INFO 2025-09-08
20:33:12.000000000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
Name: xmltodict
-Version: 0.14.2
+Version: 0.15.1
Summary: Makes working with XML feel like you are working with JSON
Home-page: https://github.com/martinblech/xmltodict
Author: Martin Blech
@@ -12,9 +12,6 @@
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
@@ -22,9 +19,20 @@
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Text Processing :: Markup :: XML
-Requires-Python: >=3.6
+Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: platform
+Dynamic: requires-python
+Dynamic: summary
# xmltodict
@@ -46,15 +54,15 @@
... """), indent=4))
{
"mydocument": {
- "@has": "an attribute",
+ "@has": "an attribute",
"and": {
"many": [
- "elements",
+ "elements",
"more elements"
]
- },
+ },
"plus": {
- "@a": "complex",
+ "@a": "complex",
"#text": "element as well"
}
}
@@ -110,7 +118,7 @@
>>> def handle_artist(_, artist):
... print(artist['name'])
... return True
->>>
+>>>
>>> xmltodict.parse(GzipFile('discogs_artists.xml.gz'),
... item_depth=2, item_callback=handle_artist)
A Perfect Circle
@@ -178,7 +186,7 @@
```python
>>> import xmltodict
->>>
+>>>
>>> mydict = {
... 'text': {
... '@color':'red',
@@ -234,7 +242,7 @@
### Using conda
-For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
+For installing `xmltodict` using Anaconda/Miniconda (*conda*) from the
[conda-forge channel][#xmltodict-conda] all you need to do is:
[#xmltodict-conda]: https://anaconda.org/conda-forge/xmltodict
@@ -286,3 +294,13 @@
# Python3
$ zypper in python3-xmltodict
```
+
+## Security Notes
+
+A CVE (CVE-2025-9375) was filed against `xmltodict` but is
[disputed](https://github.com/martinblech/xmltodict/issues/377#issuecomment-3255691923).
The root issue lies in Python’s `xml.sax.saxutils.XMLGenerator` API, which
does not validate XML element names and provides no built-in way to do so.
Since `xmltodict` is a thin wrapper that passes keys directly to
`XMLGenerator`, the same issue exists in the standard library itself.
+
+It has been suggested that `xml.sax.saxutils.escape()` represents a secure
usage path. This is incorrect: `escape()` is intended only for character data
and attribute values, and can produce invalid XML when misapplied to element
names. There is currently no secure, documented way in Python’s standard
library to validate XML element names.
+
+Despite this, Fluid Attacks chose to assign a CVE to `xmltodict` while leaving
the identical behavior in Python’s own standard library unaddressed. Their
disclosure process also gave only 10 days from first contact to
publication—well short of the 90-day industry norm—leaving no real opportunity
for maintainer response. These actions reflect an inconsistency of standards
and priorities that raise concerns about motivations, as they do not primarily
serve the security of the broader community.
+
+The maintainer considers this CVE invalid and will formally dispute it with
MITRE.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/xmltodict.egg-info/SOURCES.txt
new/xmltodict-0.15.1/xmltodict.egg-info/SOURCES.txt
--- old/xmltodict-0.14.2/xmltodict.egg-info/SOURCES.txt 2024-10-16
08:10:02.000000000 +0200
+++ new/xmltodict-0.15.1/xmltodict.egg-info/SOURCES.txt 2025-09-08
20:33:12.000000000 +0200
@@ -10,14 +10,17 @@
tests/test_xmltodict.py
tests/__pycache__/__init__.cpython-311.pyc
tests/__pycache__/__init__.cpython-312.pyc
+tests/__pycache__/__init__.cpython-313.pyc
tests/__pycache__/test_dicttoxml.cpython-310.pyc
tests/__pycache__/test_dicttoxml.cpython-311.pyc
tests/__pycache__/test_dicttoxml.cpython-312.pyc
+tests/__pycache__/test_dicttoxml.cpython-313.pyc
tests/__pycache__/test_dicttoxml.cpython-37.pyc
tests/__pycache__/test_dicttoxml.cpython-39.pyc
tests/__pycache__/test_xmltodict.cpython-310.pyc
tests/__pycache__/test_xmltodict.cpython-311.pyc
tests/__pycache__/test_xmltodict.cpython-312.pyc
+tests/__pycache__/test_xmltodict.cpython-313.pyc
tests/__pycache__/test_xmltodict.cpython-37.pyc
tests/__pycache__/test_xmltodict.cpython-39.pyc
xmltodict.egg-info/PKG-INFO
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmltodict-0.14.2/xmltodict.py
new/xmltodict-0.15.1/xmltodict.py
--- old/xmltodict-0.14.2/xmltodict.py 2024-10-16 08:06:57.000000000 +0200
+++ new/xmltodict-0.15.1/xmltodict.py 2025-09-08 20:29:07.000000000 +0200
@@ -14,7 +14,7 @@
from inspect import isgenerator
__author__ = 'Martin Blech'
-__version__ = "0.14.2"
+__version__ = "0.15.1"
__license__ = 'MIT'
@@ -360,7 +360,54 @@
return handler.item
+def _has_angle_brackets(value):
+ """Return True if value (a str) contains '<' or '>'.
+
+ Non-string values return False. Uses fast substring checks implemented in
C.
+ """
+ return isinstance(value, str) and ("<" in value or ">" in value)
+
+
+def _has_invalid_name_chars(value):
+ """Return True if value (a str) contains any disallowed name characters.
+
+ Disallowed: '<', '>', '/', or any whitespace character.
+ Non-string values return False.
+ """
+ if not isinstance(value, str):
+ return False
+ if "<" in value or ">" in value or "/" in value:
+ return True
+ # Check for any whitespace (spaces, tabs, newlines, etc.)
+ return any(ch.isspace() for ch in value)
+
+
+def _validate_name(value, kind):
+ """Validate an element/attribute name for XML safety.
+
+ Raises ValueError with a specific reason when invalid.
+
+ kind: 'element' or 'attribute' (used in error messages)
+ """
+ if not isinstance(value, str):
+ raise ValueError(f"{kind} name must be a string")
+ if value.startswith("?") or value.startswith("!"):
+ raise ValueError(f'Invalid {kind} name: cannot start with "?" or "!"')
+ if "<" in value or ">" in value:
+ raise ValueError(f'Invalid {kind} name: "<" or ">" not allowed')
+ if "/" in value:
+ raise ValueError(f'Invalid {kind} name: "/" not allowed')
+ if '"' in value or "'" in value:
+ raise ValueError(f"Invalid {kind} name: quotes not allowed")
+ if "=" in value:
+ raise ValueError(f'Invalid {kind} name: "=" not allowed')
+ if any(ch.isspace() for ch in value):
+ raise ValueError(f"Invalid {kind} name: whitespace not allowed")
+
+
def _process_namespace(name, namespaces, ns_sep=':', attr_prefix='@'):
+ if not isinstance(name, str):
+ return name
if not namespaces:
return name
try:
@@ -393,6 +440,8 @@
if result is None:
return
key, value = result
+ # Minimal validation to avoid breaking out of tag context
+ _validate_name(key, "element")
if not hasattr(value, '__iter__') or isinstance(value, (str, dict)):
value = [value]
for index, v in enumerate(value):
@@ -416,17 +465,20 @@
if ik == cdata_key:
cdata = iv
continue
- if ik.startswith(attr_prefix):
+ if isinstance(ik, str) and ik.startswith(attr_prefix):
ik = _process_namespace(ik, namespaces, namespace_separator,
attr_prefix)
if ik == '@xmlns' and isinstance(iv, dict):
for k, v in iv.items():
+ _validate_name(k, "attribute")
attr = 'xmlns{}'.format(f':{k}' if k else '')
attrs[attr] = str(v)
continue
if not isinstance(iv, str):
iv = str(iv)
- attrs[ik[len(attr_prefix):]] = iv
+ attr_name = ik[len(attr_prefix) :]
+ _validate_name(attr_name, "attribute")
+ attrs[attr_name] = iv
continue
children.append((ik, iv))
if isinstance(indent, int):