Skip to content

harden SOAPPart.setContent xml parser against xxe#1221

Merged
robertlazarski merged 1 commit into
apache:masterfrom
digi-scrypt:saaj-soappart-xxe
Jul 5, 2026
Merged

harden SOAPPart.setContent xml parser against xxe#1221
robertlazarski merged 1 commit into
apache:masterfrom
digi-scrypt:saaj-soappart-xxe

Conversation

@digi-scrypt

Copy link
Copy Markdown
  1. SOAPPartImpl.setContent builds its XMLInputFactory with newInstance() and no other settings, so DTDs and external entities are left on.
  2. when the Source is a StreamSource it's handed straight to that reader, so a body with a DOCTYPE + SYSTEM entity gets expanded — file:// reads or outbound URLs from the parser.

Set SUPPORT_DTD and IS_SUPPORTING_EXTERNAL_ENTITIES to false on the factory before it parses.

What happens if someone calls setContent with a StreamSource over an inbound stream? That's the documented SAAJ way to load a body, so the bytes are attacker reachable and the hardening has to live here rather than in the caller. Lines up with the recent WSDL/XSD XXE work that hardened the DOM path but didn't touch this StAX one. Added a SOAPPartTest case that points an external entity at a temp file and checks the contents never land in the message.

@robertlazarski robertlazarski merged commit 2b2888b into apache:master Jul 5, 2026
5 checks passed
@robertlazarski

Copy link
Copy Markdown
Contributor

Merged — thanks, and welcome as a contributor! 🎉

The fix is correct and lands in exactly the right spot. setContent(Source)
was the one SAAJ parse path that built its own XMLInputFactory and created
the reader directly, so it bypassed the DTD guard that Axiom otherwise
applies to SOAP parsing (StAXParserConfiguration.SOAP rejects any DOCTYPE).
Disabling SUPPORT_DTD here closes that gap at the root, and
IS_SUPPORTING_EXTERNAL_ENTITIES=false is good defense-in-depth on top. I
confirmed the inbound-message paths (MessageFactory.createMessage(...,
InputStream) and the MTOM branch) already route through Axiom's hardened
builder, so this was the right — and only — path that needed attention.

This also can't break a legitimate caller: SOAP 1.1 and 1.2 both prohibit
DTDs in the envelope, so a spec-compliant message never carries a DOCTYPE.

The test is well done too — pointing a SYSTEM entity at a temp file and
asserting the marker never appears in the serialized message is the right
check, and accepting either "rejected with an exception" or "not expanded"
as a pass is exactly correct.

Appreciate the clear write-up on the PR. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants