XML parser created without XXE hardening
ID |
kotlin.insecure_xml_parser_configuration |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Misconfiguration |
Language |
Kotlin |
Tags |
ASVS50:v1.5.1, CWE:611, CWE:776, OWASP:2025:A02 |
Description
An XML parser or parser factory is created and no setting hardening it against XML External Entity (XXE) processing is applied to it. Left at its defaults, a JAXP, StAX or TrAX parser resolves the DTDs and external entities declared in the documents it parses. Kotlin uses the same Java factories, so it inherits the same defaults.
Rationale
kotlin.xml_entity_injection reports XXE when a data path can be followed from an external input to
the parser. That is the right answer for application code, but it needs the input to be visible: a
parser built in a codec, a utility object or a shared library, where the caller lives in another
project, has no source to follow and is reported by nothing.
The parser setup is unsafe on its own terms, independent of who calls it. This detector reports the
setup, so a library that hands an unhardened parser to its callers is visible without having to guess at
reachability. Its severity is low because the finding says nothing about whether attacker-controlled XML
actually reaches the parser — when that can be shown, kotlin.xml_entity_injection reports it as
critical, and this finding is dropped as a duplicate.
What counts as hardening
A parser is considered hardened when setFeature, setAttribute or setProperty applies any
XXE-relevant setting to it — disallow-doctype-decl, external-general-entities,
external-parameter-entities, load-external-dtd, secure processing, accessExternalDTD,
accessExternalSchema, accessExternalStylesheet, supportDTD or isSupportingExternalEntities — written either as the literal feature URI or as the corresponding XMLConstants / XMLInputFactory
constant.
The value passed is deliberately not inspected. The question this detector answers is whether the parser was hardened at all; a setting deliberately pushed to the unsafe value is a different defect.
Kotlin reaches the Java setters through property syntax, and
isExpandEntityReferences, isValidating, isNamespaceAware and isXIncludeAware do not count.
expandEntityReferences is true by default in JAXP, so writing it explicitly changes nothing, and
setting it to false does not stop the parser from resolving external entities — it only stops them
being expanded into the tree. Accepting it as hardening would hide the defect it looks like it fixes.
Examples
Reported — no hardening anywhere on the factory:
fun decode(file: File): Document {
val dbf = DocumentBuilderFactory.newInstance() // reported
return dbf.newDocumentBuilder().parse(file)
}
Not reported — the factory carries a hardening setting:
fun decode(file: File): Document {
val dbf = DocumentBuilderFactory.newInstance()
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
return dbf.newDocumentBuilder().parse(file)
}
Remediation
Disable DTD processing entirely where the documents parsed do not need it:
val dbf = DocumentBuilderFactory.newInstance()
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
When a DTD must be accepted, turn off external entity resolution instead:
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
For TransformerFactory and SchemaFactory, deny external access:
val tf = TransformerFactory.newInstance()
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "")
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "")
For StAX:
val xif = XMLInputFactory.newInstance()
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false)
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false)