Skip to content

VNX-776: Improper Restriction of Recursive Entity References in DTDs ('XML Entity Expansion')

Untrusted XML parsed by Python standard-library parsers or xmltodict, and libxml2 expansion limits switched off (lxml huge_tree, LIBXML_PARSEHUGE, libxmljs huge).

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-776
Severity Medium
SARIF level warning
Kind sast
Languages Python, PHP, JavaScript / TypeScript
CWE CWE-776
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags xml-bomb billion-laughs cwe-776
Source internal/sast/rules/vnx-776.rego

Overview

VNX-776 maps to CWE-776: Improper Restriction of Recursive Entity References in DTDs (‘XML Entity Expansion’). It reports XML parsing that expands nested internal entities without a limit:

  • Python xml.etree.ElementTree / cElementTree (fromstring, XML, parse, iterparse, fromstringlist), xml.dom.minidom and pulldom (parse, parseString), xml.sax and xmltodict.parse, under any alias, when given request data, an upload stream, stdin, urlopen() or a requests response, directly or through a variable assigned from one up to 25 lines above
  • lxml XMLParser(huge_tree=True), PHP LIBXML_PARSEHUGE, libxmljs huge: true and libxml2-wasm XML_PARSE_HUGE, which switch libxml2’s expansion limits off

Not reported: defusedxml (or a file calling defusedxml.defuse_stdlib()), parsers given a constant, a local path or a parameter of unknown origin, lxml with its default limits, Java factories (VNX-JAVA-024) and external entity resolution (XXE).

Why This Matters

A few kilobytes of nested entity definitions (“billion laughs”, quadratic blowup) expand to gigabytes in memory, so one request exhausts the worker’s memory and CPU.

What Gets Flagged

root = ET.fromstring(request.data)            # FLAGGED
dom = minidom.parseString(request.body)       # FLAGGED
parser = etree.XMLParser(huge_tree=True)      # FLAGGED
$doc->loadXML($xml, LIBXML_PARSEHUGE);        // FLAGGED

Not flagged:

from defusedxml import ElementTree as ET
root = ET.fromstring(request.data)

Remediation

  1. Parse untrusted XML in Python with defusedxml, or with lxml at its default limits.
  2. Remove huge_tree=True, LIBXML_PARSEHUGE, huge: true and XML_PARSE_HUGE from code that parses untrusted input.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-776
// nosec vnx-776

Or record an ignore rule: vulnetix ignore add --rule vnx-776 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-776.rego, cli/internal/sast/docs/vnx-776.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.