Mesrai
Back to blog
// essaySecurity

XXE Injection in XML Parsers: Mesrai's Default-Off Detection

Real PR walkthrough: enabling `noent` and `dtdload` on an XML parser opens XXE. Mesrai's catch, the file-read attack, the safe parser config. CWE-611.

Mesrai TeamJuly 18, 20269 min read

A legacy SOAP integration on the backend. The team needed entity expansion for one specific payload format. They enabled it globally on the parser. Mesrai's review comment a few minutes after PR open: XXE.

The vulnerable diff

tsintegrations/legacy-soap.ts
// integrations/legacy-soap.ts
import libxmljs from "libxmljs";

soapRouter.post("/inbound", async (req, res) => {
  // BUG: noent expands entities, dtdload fetches external DTDs
  const doc = libxmljs.parseXml(req.body, {
    noent: true,
    dtdload: true,
  });
  const payload = doc.get("//Payload")?.text();
  res.json({ ok: true, payload });
});

What is wrong

XML supports a feature called external entities — special declarations in the document's DTD that reference external resources. With entity expansion on (`noent: true`) and external loading on (`dtdload: true`), the parser will resolve those references at parse time. Attackers craft an entity that points at a local file or an internal URL, then trigger expansion by referencing the entity in the document body. This is CWE-611, one of the oldest known XML attacks.

The attack

xmlattack.xml
<?xml version="1.0"?>
<!DOCTYPE soap [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Payload>&xxe;</Payload>
  </soap:Body>
</soap:Envelope>

The response from the vulnerable endpoint includes the contents of `/etc/passwd` in `payload`. Swap the file URL for an internal HTTP URL and you get SSRF too — the parser will fetch internal services on the attacker's behalf. Blind-XXE variants exfiltrate via DNS lookups even when the response is empty.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 3 min ago

[mesrai] [code-review] [Security] [CWE-611] [high]

`noent: true` + `dtdload: true` on `libxmljs.parseXml` enables XXE.
Attacker XML with `<!ENTITY xxe SYSTEM "file:///etc/passwd">` reads
arbitrary server-readable files; SYSTEM URLs to internal services
turn this into SSRF.

The libxmljs2 defaults are safe — both options off. Remove them
unless you have a documented reason and a content-validation layer:

  const doc = libxmljs.parseXml(req.body, {
    noent: false,
    dtdload: false,
    noblanks: true,
  });

If you genuinely need entity expansion for a partner format, isolate
that one code path, document the threat model, and gate behind an
allowlist of payload sources.

Reference: OWASP XXE Prevention Cheat Sheet, CWE-611

The fix

tsintegrations/legacy-soap.ts (fixed)
// integrations/legacy-soap.ts — fixed
const doc = libxmljs.parseXml(req.body, {
  noent: false,
  dtdload: false,
  noblanks: true,
});

// Strict size + element count guard to prevent billion-laughs DoS
if (req.body.length > 256 * 1024) {
  return res.status(413).json({ error: "payload too large" });
}

Defaults off, plus a payload size cap to defend against the related billion-laughs attack (recursive entity expansion that explodes memory). If a specific integration needs entity expansion, gate it behind an allowlist of source IPs and document the residual risk in the code.

Why human review missed it

XXE is one of those bugs that is well-known among security specialists and largely invisible to product engineers. The parser flags read like ergonomic settings — `noent` sounds like "no entity errors," not "expand external entities." The author had set the flags following a Stack Overflow answer from 2014 and the original reviewer trusted the library defaults. Mesrai's security rule pack catches the pattern regardless of comment context.

Related rules + further reading

Mesrai rule pack: security/no-xxe — flags XML parsers with external entity or DTD loading enabled.

OWASP XXE Prevention Cheat Sheet — language-specific safe-parser configs.

CWE-611 — Improper Restriction of XML External Entity Reference.

Takeaway

XML parsers default to safe in 2026 for a reason. If you have to turn entity expansion on, you also have to own the threat model that comes with it. Mesrai's job is to make sure that choice is explicit, never accidental.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.