Skip to content

Comments

Add XXE query for Rust (rust/xxe, CWE-611)#21352

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/add-xxe-query-for-rust
Draft

Add XXE query for Rust (rust/xxe, CWE-611)#21352
Copilot wants to merge 3 commits intomainfrom
copilot/add-xxe-query-for-rust

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

Adds a new @precision high / @severity error path-problem query detecting XML External Entity (XXE) vulnerabilities in Rust code that uses the libxml crate (bindings to C's libxml2). Pure-Rust XML crates are safe by default; only libxml is a realistic target via XML_PARSE_NOENT (value 2) or XML_PARSE_DTDLOAD (value 4).

Query logic

  • Source: ActiveThreatModelSource (network, env, stdin, etc.)
  • Sink: XML content argument of libxml2 parsing functions when the options argument contains unsafe flags — detected via named constants, decimal integer literals with bits 2/4 set, bitwise OR expressions, and cast expressions
  • Functions modeled: xmlReadFile, xmlReadMemory, xmlReadDoc, xmlReadFd, xmlCtxtReadFile, xmlCtxtReadDoc, xmlCtxtReadFd, xmlCtxtReadMemory, xmlReadIO, xmlCtxtReadIO, xmlParseInNodeContext
// BAD: XML_PARSE_NOENT enables external entity expansion
let user_xml = std::env::args().nth(1).unwrap();
xmlReadMemory(&user_xml, user_xml.len() as i32, "", "", XML_PARSE_NOENT); // Alert

// GOOD: safe options disable entity expansion
xmlReadMemory(&user_xml, user_xml.len() as i32, "", "", 0); // No alert

// GOOD: dangerous options but hardcoded (non-tainted) XML
xmlReadMemory("<root/>", 7, "", "", XML_PARSE_NOENT); // No alert

Files

  • rust/ql/lib/codeql/rust/security/XxeExtensions.qll — sources, sinks, barriers
  • rust/ql/src/queries/security/CWE-611/Xxe.ql — main query (CWE-611, CWE-776, CWE-827)
  • rust/ql/src/queries/security/CWE-611/Xxe.qhelp — documentation and examples
  • rust/ql/test/query-tests/security/CWE-611/ — test suite with stub libxml2 functions covering bad/good cases
  • rust/ql/src/change-notes/2026-02-20-xxe.md

Note: xmlCtxtUseOptions is intentionally excluded — it configures an existing parser context and has no XML content argument, so treating it as a sink would produce false positives.

Original prompt

Create a new query for XXE ("XML eXternal Entities") vulnerabilities in Rust. This will provide coverage for CWE-611 and OWASP A05.

See issue: https://github.com/github/codeql-c-team/issues/2865

Background

XXE vulnerabilities occur when an XML parser is configured to process external entity references while parsing user-controlled XML input. In the Rust ecosystem, most pure-Rust XML crates (quick-xml, xml-rs, roxmltree, etc.) do NOT support external entity expansion and are safe by default. The only realistic target is the libxml crate (Rust bindings to C's libxml2), which can be configured to expand external entities via parsing options like XML_PARSE_NOENT and XML_PARSE_DTDLOAD.

MRVA analysis of the top 1000 projects confirms this is very rare in practice - only one project even defines these constants and doesn't use them. Therefore, keep the implementation minimal and focused on just the libxml bindings.

Plan (in order)

Step 1: Write test cases FIRST

Create test Rust code in rust/ql/test/query-tests/security/CWE-611/ that exercises the libxml2 bindings:

  • Bad cases: Calling libxml2 parsing functions (xmlReadFile, xmlReadMemory, xmlReadDoc, xmlCtxtReadFile, xmlCtxtReadDoc, xmlCtxtReadMemory, xmlReadFd, xmlCtxtReadFd, xmlReadIO, xmlCtxtUseOptions) with XML_PARSE_NOENT or XML_PARSE_DTDLOAD in the options, where the XML input comes from a remote/taint source. These should produce alerts.
  • Good cases:
    • Same parsing calls but with 0 or safe options (no entity expansion enabled) — should NOT produce alerts.
    • Using the vulnerable options but with non-tainted (hardcoded/local) input — should NOT produce alerts.
  • Include a Cargo.toml for the test project.
  • Use inline test expectations ($ Alert, $ Source=, sink annotations) following the established pattern in other Rust security query tests (see rust/ql/test/query-tests/security/CWE-022/ for the pattern).

Step 2: Add the XXE Extensions library

Create rust/ql/lib/codeql/rust/security/XxeExtensions.qll following the established pattern used by other Rust security queries (e.g., TaintedPathExtensions.qll, CleartextTransmissionExtensions.qll):

  • Define abstract classes Source, Sink, and Barrier.
  • Source should default to ActiveThreatModelSource (from codeql.rust.Concepts).
  • Sink should model the XML input argument of libxml2 parsing functions when XML_PARSE_NOENT or XML_PARSE_DTDLOAD appears in the options argument.
  • Model the specific libxml2 parsing functions. Reference the existing C++ model at cpp/ql/src/Security/CWE/CWE-611/Libxml2.qll and Swift model at swift/ql/lib/codeql/swift/frameworks/Xml/Libxml2.qll for the list of functions and which argument positions hold the XML input vs options. The key functions and their options argument positions are:
    • xmlCtxtUseOptions — options at arg 1
    • xmlReadFile — options at arg 2
    • xmlCtxtReadFile, xmlParseInNodeContext, xmlReadDoc, xmlReadFd — options at arg 3
    • xmlCtxtReadDoc, xmlCtxtReadFd, xmlReadMemory — options at arg 4
    • xmlCtxtReadMemory, xmlReadIO — options at arg 5
    • xmlCtxtReadIO — options at arg 6
  • The sink is the XML content argument of these functions (typically arg 0, or arg 1 for some xmlCtxt* variants).
  • The query should flag calls where the options include XML_PARSE_NOENT (value 2) or XML_PARSE_DTDLOAD (value 4), which enable external entity processing.

Step 3: Write the query

Create rust/ql/src/queries/security/CWE-611/Xxe.ql:

  • Use @id rust/xxe
  • @kind path-problem
  • @problem.severity error
  • @security-severity 9.1
  • @precision high
  • Tags: security, external/cwe/cwe-611, external/cwe/cwe-776, external/cwe/cwe-827
  • Follow the standard Rust taint tracking pattern:
    module XxeConfig implements DataFlow::ConfigSig {
      predicate isSource(DataFlow::Node src) { src instanceof Source }
      predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
      predicate isBarrier(DataFlow::Node barrier) { barrier instanceof Barrier }
    }
    module XxeFlow = TaintTracking::Global<XxeConfig>;
    
  • Select statement: "XML parsing depends on a $@ without guarding against external entity expansion."

Step 4: Documentation

  • Create rust/ql/src/queries/security/CWE-611/Xxe.qhelp with a description of XXE, risk explanation, and recommendation.
  • Create good/bad Rust example files in rust/ql/src/queries/security/CWE-611/examples/.
  • Add a change note in rust/ql/src/change-notes/ for the new query.

Reference implementations to study

  • Ruby XXE query (simplest pattern): ruby/ql/src/queries/security/cwe-611/Xxe.ql and ruby/ql/lib/codeql/ruby/frameworks/XmlParsing.qll
  • Swift XXE query: swift/ql/src/queries/Security/CWE-611/XXE.ql and swift/ql/lib/codeql/swift/security/XXEExtensions.qll and `swift/ql/lib/codeql/swift/frameworks/Xml/Libxml2.qll...

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 20, 2026 18:22
Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
Co-authored-by: geoffw0 <40627776+geoffw0@users.noreply.github.com>
Copilot AI changed the title [WIP] Add query for XXE vulnerabilities in Rust Add XXE query for Rust (rust/xxe, CWE-611) Feb 20, 2026
Copilot AI requested a review from geoffw0 February 20, 2026 18:35
@geoffw0 geoffw0 added the Rust Pull requests that update Rust code label Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants