# XXE / unsafe-XML-parser pre-scan (ARION service)

Deterministic scanner for the XML-external-entity family across Python,
Java/Kotlin/Groovy, C#, PHP, Ruby and JS/TS sources: (1) parsers whose
**default resolves external entities** with no visible hardening
(`DocumentBuilderFactory`, `SAXParserFactory`, `XMLInputFactory`,
`XMLReaderFactory`, dom4j `SAXReader`/`DocumentHelper`, jdom
`SAXBuilder`, commons `Digester`, `xml.sax`, lxml `etree`,
`XmlTextReader`), (2) **explicit entity-enabling options**
(`resolve_entities=True`, `LIBXML_NOENT`, `noent:true`,
`ParseOptions::NOENT`, `DtdProcessing.Parse`, `XmlUrlResolver`,
`libxml_disable_entity_loader(false)`, `setExpandEntityReferences(true)`,
`setXIncludeAware(true)`, `SUPPORT_DTD`/`isSupportingExternalEntities`
set true, sax `external-*-entities` features set true,
`FEATURE_SECURE_PROCESSING` false), (3) **DTD-accepting parsers** where
internal-entity expansion (billion laughs / quadratic blowup) applies —
`minidom`, `pulldom`, `xml.etree`, raw `expat`, `REXML`, `Nokogiri`,
`simplexml_*`, `DOMDocument->loadXML`, `xml_parse`, `XmlDocument.Load`,
`XmlReader.Create`, JAXB `unmarshal`, `.xinclude()` — and (4)
**unrestricted external-resource access** on `TransformerFactory`/
`SchemaFactory`/`XPathFactory` (no `ACCESS_EXTERNAL_*` lockdown).
No server, no network — every check is a property of the construction
line plus a bounded hardening window.

**Honest scope:** this is a *pre-scan*, not an audit. It cannot prove
that untrusted input reaches the parse call — a FLAG means "this parser
construction/option can resolve or expand entities; review the input
path and the hardening". DTD grammar analysis, runtime parser-version
checks and upstream proxy validation are out of scope. A clean report
is not proof the codebase is XXE-safe.

## Coverage

- **`XXE-EXT-OPT` (HIGH, never downgraded)** — an explicit
  entity/DTD-enabling option on the line. These are opt-ins: the code
  asked for entity resolution. Subsumes softer findings on the same
  line.
- **`XXE-DEFAULT-PARSER` (HIGH)** — parser/factory construction that
  fetches external entities out of the box: Java
  `DocumentBuilderFactory`/`SAXParserFactory`/`XMLInputFactory`/
  `XMLReaderFactory`, dom4j/jdom/digester/`DocumentHelper.parseText`,
  python `xml.sax.parse/parseString/make_parser`, lxml
  `etree.parse/fromstring/XML/XMLParser` (and bare `XMLParser(` when
  lxml is imported), C# `new XmlTextReader`.
- **`XXE-DTD-ENABLED` (MEDIUM)** — internal-entity expansion or
  unclear framework default: `minidom`/`pulldom`, `xml.etree`/`ET`,
  `expat.ParserCreate`, `REXML`, `Nokogiri::XML`, `simplexml_load_*`/
  `SimpleXMLElement`/`simplexml_import_dom`, `DOMDocument->load/loadXML`,
  `xml_parse`/`xml_parser_create`, `XmlDocument`/`LoadXml`/`XmlReader.
  Create`, JAXB `unmarshal`, lxml `.xinclude()`.
- **`XXE-EXT-ACCESS` (MEDIUM)** — `TransformerFactory`/
  `SAXTransformerFactory`/`SchemaFactory`/`XPathFactory` construction
  without a visible `ACCESS_EXTERNAL_DTD|SCHEMA|STYLESHEET` lockdown —
  external stylesheet/schema fetch even when entity parsing is safe.

## Context rules (false-positive controls)

- **Hardening downgrades, never suppresses** — markers within a ±8-line
  window (`setFeature(...,false)` / `setFeature(disallow-doctype-decl|
  SECURE_PROCESSING,...)`, `setProperty(...,false|ACCESS_EXTERNAL|...)`,
  `setAttribute(XMLConstants|http...)`, `ACCESS_EXTERNAL_*`,
  `DtdProcessing.Prohibit|Ignore`, `XmlSecureResolver`,
  `XmlResolver=null`, `setXIncludeAware(false)`,
  `setExpandEntityReferences(false)`, `resolve_entities=False`,
  `no_network`/`nonet`, `external-*-entities` false) tier a finding LOW
  for review. One hardened call site cannot whitewash unhardened
  factories elsewhere in the file.
- **Request-global calls apply file-wide** —
  `libxml_disable_entity_loader(true)`, `forbid_dtd`/`forbid_external`.
- **`defused` imports silence the python findings entirely** —
  defusedxml is the fix.
- **Import-gated receivers** — `etree.`/`ET.` read as lxml only when
  `lxml` is imported (stdlib `xml.etree` otherwise); `sax.`/`make_parser`
  gated on `xml.sax`; `expat.`/`ParserCreate` on `xml.parsers.expat`;
  `noent:true` on a real `require/import libxmljs`; PHP `->load(` on
  `DOMDocument` presence; C# `.Load(` on `Xml*` types.
- **Comments are stripped before marker scanning** — a `// TODO use
  defusedxml` note does not silence or downgrade anything.
- Test/spec/fixture paths downgrade one tier; `def make_parser(`
  definitions are skipped; comments (`//`, `#`, `/* */`, `<!-- -->`,
  `{#`) are skipped.

## Validation

- 69 selftest vectors (`--selftest`) covering every code, both
  languages' opt forms, hardening downgrades, comment/import
  suppression, file-global vs windowed hardening and receiver gating.
- Hostile 6-file fixture (`sample-repo/`, Java+Python+PHP+C#+Ruby+JS):
  all 15 hostile patterns flagged at the right tier; the hardened
  factory and clean lines silent or correctly LOW.
- Live-validated on a real 253-file TS/JS service repo: **zero
  findings** — no scanner noise. On the CPython 3.11 stdlib (542
  files): 13 findings, all genuine parser-construction points in
  `xml/sax`, `xml/dom`, `xml/etree`, `plistlib` and `xmlrpc` — the
  stdlib modules that ARE the parsers.

## Usage

```
python3 xxe_prescan.py <file-or-dir> [--json]
python3 xxe_prescan.py --selftest
```

Pure stdlib, Python 3.8+. Exit 0 = no HIGH/MEDIUM findings, 1 =
findings, 2 = usage error.

Built by ARION (autonomous agent; machine-produced, self-verified).
