XXE - XML External Entity Processing
An XML parser is left in a configuration that resolves external entities, so a document the attacker controls can name a SYSTEM entity and make the parser read a local file or issue a request from the server. Findings are reported under API8:2023 Security Misconfiguration.
What Vulkro detects
Vulkro runs several parser-specific XXE detectors. All of them are affirmative configuration shapes rather than taint proofs: the detector reads the parser construction, it does not run the parser.
Python: an lxml.etree.XMLParser constructed with any of resolve_entities=True, no_network=False, or load_dtd=True (High severity, High confidence, no taint required). XXE-PY-001 (High) covers an xml.sax parser that affirmatively enables the external general or parameter entity feature, and is suppressed when the file imports defusedxml.
JavaScript: XXE-JS-001 (High) fires on a libxmljs / libxmljs2 parse called with noent: true, which turns on libxml2 entity substitution. XXE-JS-002 (Medium, heuristic) fires on xmldom / @xmldom/xmldom DOMParser().parseFromString(...), which is Medium precisely because the installed runtime version cannot be read from source.
Go: XXE-GO-001 (Medium, heuristic) fires on a parse through a third-party libxml2 binding. The stdlib encoding/xml never resolves external SYSTEM or DTD entities, so a binding is the only Go XXE vector, and reachability with untrusted input is not proven.
Java: JAVA-XXE-001 (High severity, Medium confidence) fires when a JAXP DocumentBuilderFactory / SAXParserFactory / XMLInputFactory / TransformerFactory, a dom4j SAXReader, or a JDOM SAXBuilder parses a request-derived value in a file where no hardening feature is set anywhere. If any hardening call is present in the same file the detector stays silent, because a single textual pass cannot prove which parser instance it guards.
What this does not claim: a finding says the parser is configured to resolve external entities (or, for Java, that no hardening is visible in the file), not that a working exploit exists. The entity-limit / billion-laughs denial-of-service variant is a separate, lower-severity reliability signal.
Remediation. Use the hardened wrapper for your language: defusedxml in Python, a factory with disallow-doctype-decl and FEATURE_SECURE_PROCESSING set in Java, libxml2 parsed without NOENT and with network access off, or the Go stdlib encoding/xml.
Non-compliant code (examples)
Python - lxml parser with every protection flipped off
from lxml import etree
parser = etree.XMLParser(resolve_entities=True, load_dtd=True, no_network=False)
doc = etree.fromstring(request_body, parser) # SYSTEM entities resolve
Compliant code (examples)
Python - defusedxml, or lxml with the knobs set safely
from defusedxml.lxml import fromstring
doc = fromstring(request_body)
# If lxml must stay, be explicit:
# parser = etree.XMLParser(resolve_entities=False, load_dtd=False, no_network=True)
See also
- Confidence model - what
High,Medium, andLowmean for findings in this category. - Safety - what Vulkro does and does not access on your machine.
References
This page is generated by vulkro rules export <out-dir> from Vulkro's built-in detector catalogue. Edits made by hand are overwritten on the next regeneration.