Prevent XXE in the HL7 v2.x strict parser - #408
Merged
tonygermano merged 2 commits intoSep 24, 2026
Merged
tonygermano merged 2 commits into
tonygermano merged 2 commits into
Conversation
pacmano1
requested review from
a team,
gibson9583,
jonbartels,
kayyagari,
kpalang,
mgaffigan,
ssrowe and
tonygermano
August 10, 2026 19:31
pacmano1
force-pushed
the
fix/hl7v2-strict-parser-xxe
branch
from
September 10, 2026 19:58
fefe2ea to
fcac881
Compare
jonbartels
previously approved these changes
Sep 23, 2026
jonbartels
force-pushed
the
fix/hl7v2-strict-parser-xxe
branch
from
September 23, 2026 17:57
fcac881 to
9682f76
Compare
mgaffigan
previously approved these changes
Sep 24, 2026
gibson9583
requested changes
Sep 24, 2026
gibson9583
left a comment
Contributor
There was a problem hiding this comment.
silently drops CDATA content. Its replacement parser in ER7Serializer.java (~line 540) doesn't call setCoalescing(true)
The strict parser hands XML-encoded inbound messages to HAPI 2.3, whose XMLUtils.parse resolves external XML entities. On a channel with the strict parser and strict validation enabled, an unauthenticated message to the MLLP/TCP listener could trigger SSRF and local file disclosure. Override CustomDefaultXMLParser.parseStringIntoDocument -- the sole path to the vulnerable parse -- to reject DOCTYPE declarations, matching the disallow-doctype-decl hardening already used on the fromXML path. Legitimate HL7 v2.xml is schema-based and never carries a DOCTYPE, so no valid message is affected and the strict parser keeps accepting XML as before. Verified with a live MLLP reproduction (xxe-poc): the unpatched build fetched the attacker URL; the patched build rejects the message and never calls out. Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com>
The DOCTYPE-rejecting parser override did not coalesce CDATA into text nodes. HAPI reads only text nodes into a field, so a field sent as CDATA was silently emptied, where HAPI's own parser kept it. Enable coalescing. Adds a unit test and a smoke case (111-hl7-strict-xml-cdata) that sends a v2.xml message with a CDATA field through a strict-parser channel. Signed-off-by: Finnegan's Owner <44065187+pacmano1@users.noreply.github.com>
pacmano1
force-pushed
the
fix/hl7v2-strict-parser-xxe
branch
from
September 24, 2026 18:49
9682f76 to
03eefcf
Compare
Contributor
Author
|
Reproduced on a running engine. A strict-parser channel given |
gibson9583
approved these changes
Sep 24, 2026
mgaffigan
approved these changes
Sep 24, 2026
jonbartels
approved these changes
Sep 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The HL7 v2.x strict parser hands XML-encoded inbound messages to the bundled HAPI 2.3, whose
XMLUtils.parseresolves external XML entities with no hardening. On a channel with Use Strict Parser + Validate in Strict Parser, an unauthenticated message to the MLLP/TCP listener could trigger SSRF (internal services, cloud metadata) and local file disclosure. Reported as oie-1; also independently reported by Samuel Paschuan.Fix
ER7Serializer.CustomDefaultXMLParser.parseStringIntoDocument— the sole path from OIE to the vulnerable parser — is overridden to rejectDOCTYPEdeclarations, matching thedisallow-doctype-declhardening already used on the siblingfromXMLpath (and ~20 other XML parse sites in the tree).This is a minimal, non-breaking change. The format the strict parser handles — the official HL7 v2.xml encoding (
urn:hl7-org:v2xml) — is defined by XML Schema, not DTDs, so a standards-conformant message does not use a DOCTYPE and is unaffected. (This is distinct from Mirth's own non-strict<HL7Message>format, which is a separate code path the override does not touch, and which is already DOCTYPE-hardened on thefromXMLside.) Any inbound message that does carry a DOCTYPE is now rejected rather than parsed — that is the intended hardening, since at the parser level a DOCTYPE-bearing message is indistinguishable from the attack. Existing channels need no changes: no new options, no migration, no configuration.How this was verified
hapi-base-2.3.jar:XMLUtilssets no doctype or external-entity restrictions.parseStringIntoDocumentis the only caller ofXMLUtils.parseacross all HAPI parser classes, and both of OIE's parser instances (serialization + deserialization) are the overriddenCustomDefaultXMLParser— it is the only HAPI XML parser instantiated in the server/donkey tree.xxe-pocchannel (MLLP :6661, strict + strict validation), sent the report's payload against an out-of-band HTTP catcher:DOCTYPE is disallowed— the message is rejected and the server never calls out.Tests
Unit tests fire the report's payload on the strict
toXMLpath and assert the parse is rejected with aDOCTYPE-disallowed root cause, alongside a benign v2.xml message that still round-trips (the hardening does not break valid XML).A CDATA field in a strict-parsed v2.xml message keeps its value. This is covered by
testToXmlStrictValidatingKeepsCdataContentand by smoke case111-hl7-strict-xml-cdata, which checks the stored transformed message on a running engine.Honest caveat: the DOCTYPE-rejection unit test asserts the intended behavior but does not by itself discriminate patched from unpatched, because the test-JVM's default XML parser rejects a DOCTYPE regardless — the engine runtime is where HAPI resolves a different, vulnerable parser. The discriminating before/after evidence is the live MLLP reproduction above.
Follow-ups (separate)
hapi-structures2.3 → 2.6.0 bump is tracked by Renovate in the Dependency Dashboard (Dependency Dashboard #386); the vendoredhapi-basejar needs replacing alongside it. This override should stay even after the upgrade: HAPI's 2.4+ fix leavesdisallow-doctype-decl=false(permits a DOCTYPE, only disables entity resolution), so this refusal is the stronger posture.XMLBatchAdaptorparses inbound content with an unhardenedXPathFactory) was found during this work and will be filed and fixed separately.Credits
Thanks to Samuel Paschuan for reporting the issue, and to Michael Gaffigan (@mgaffigan) and Tony Germano for the design discussion.