CVE-2025-9375 Overview
CVE-2025-9375 is an XML Injection vulnerability [CWE-91] in the xmltodict Python library affecting versions from 0.14.2 before 0.15.1. The flaw resides in the xmltodict.unparse() function, which fails to validate element and attribute names before serializing them to XML. An attacker who controls dictionary keys passed to unparse() can inject characters that break out of the intended XML structure and manipulate the resulting document. The vendor disputes the scope of this CVE, arguing that name-handling is delegated to Python's xml.sax.saxutils.XMLGenerator, which should perform validation. The fix landed in commit f98c90f and was released in xmltodict 0.15.1.
Critical Impact
Attackers who control dictionary keys passed to xmltodict.unparse() can manipulate XML output, potentially altering downstream parsing logic, configuration files, or message payloads consumed by other services.
Affected Products
- xmltodict Python library versions 0.14.2 through versions prior to 0.15.1
- Applications that pass untrusted input as dictionary keys to xmltodict.unparse()
- Downstream consumers parsing XML produced by vulnerable xmltodict versions
Discovery Timeline
- 2025-09-01 - CVE-2025-9375 published to NVD
- 2026-04-20 - Last updated in NVD database
Technical Details for CVE-2025-9375
Vulnerability Analysis
The vulnerability is classified as XML Injection [CWE-91]. xmltodict.unparse() converts Python dictionaries into XML documents but does not validate that dictionary keys are well-formed XML names. When attacker-controlled input becomes a key, characters such as <, >, /, or whitespace are written directly into element and attribute name positions. This allows the attacker to terminate tags early, inject new elements, or fabricate attributes, producing XML that differs structurally from what the developer intended. The vendor has disputed the CVE on the basis that name validation should occur in xml.sax.saxutils.XMLGenerator rather than in xmltodict.
Root Cause
The root cause is missing input validation on XML name positions. Prior to 0.15.1, unparse() trusted the caller to provide syntactically valid element and attribute names and relied on the underlying SAX generator to escape content rather than reject malformed names. Because XML name positions are not subject to the same escaping as text content, any disallowed character placed in a key passes through unchanged.
Attack Vector
Exploitation requires the application to feed attacker-controlled data into a dictionary key that is later serialized with xmltodict.unparse(). Typical scenarios include web applications that accept JSON or form fields and convert them to XML for downstream APIs, configuration generators, or SOAP clients. The attack vector is network-reachable wherever such input is accepted.
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')
Source: xmltodict Commit f98c90f. The patch introduces _has_invalid_name_chars() and _validate_name() helpers that reject names containing <, >, /, whitespace, or that begin with ? or !.
Detection Methods for CVE-2025-9375
Indicators of Compromise
- XML payloads produced by the application that contain unexpected element or attribute names, especially names containing <, >, /, or whitespace characters.
- Downstream parser errors or schema validation failures originating from services that consume XML generated by xmltodict.unparse().
- Application logs showing user-controlled field names being passed as keys into unparse() calls.
Detection Strategies
- Inventory Python projects and lock files (requirements.txt, Pipfile.lock, poetry.lock) for xmltodict versions 0.14.2 through 0.15.0.
- Perform static analysis to locate calls to xmltodict.unparse() and trace whether dictionary keys are derived from untrusted input.
- Add unit tests that pass keys containing <, >, /, and whitespace to unparse() and confirm the patched version raises ValueError.
Monitoring Recommendations
- Monitor outbound XML traffic from services using xmltodict for structural anomalies and unexpected tag names.
- Alert on application exceptions referencing xmltodict.unparse or xml.sax after upgrading, since the patch raises ValueError on invalid names.
- Track Software Bill of Materials (SBOM) data to flag deployments still running affected xmltodict versions.
How to Mitigate CVE-2025-9375
Immediate Actions Required
- Upgrade xmltodict to version 0.15.1 or later in all affected projects and rebuild dependent container images.
- Audit application code for any path where untrusted input can become a dictionary key passed to xmltodict.unparse().
- Add server-side validation that restricts XML element and attribute names to a known allowlist before serialization.
Patch Information
The issue is fixed in xmltodict 0.15.1 via commit f98c90f. The patch adds _has_invalid_name_chars() and _validate_name() to reject element and attribute names containing <, >, /, whitespace, or leading ?/! characters. See the xmltodict Changelog v0.15.1 and the Fluid Attacks Advisory for additional context.
Workarounds
- If upgrading is not immediately possible, sanitize all dictionary keys before calling unparse() by rejecting strings containing <, >, /, or whitespace.
- Avoid using user-supplied identifiers as XML element or attribute names; map them to a fixed schema instead.
- Wrap calls to xmltodict.unparse() in a helper that validates keys against the XML Name production rules defined in the W3C XML specification.
# Configuration example: pin a fixed, patched version of xmltodict
pip install --upgrade "xmltodict>=0.15.1"
# Verify the installed version
python -c "import xmltodict; print(xmltodict.__version__)"
# Optional: enforce minimum version in requirements.txt
echo 'xmltodict>=0.15.1' >> requirements.txt
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

