Skip to content

fix(sanitizer): close case-insensitive scheme and scheme-relative URL bypasses in exfiltration guard#6518

Merged
bug-ops merged 2 commits into
mainfrom
fix/6508-exfil-scheme-bypass
Jul 19, 2026
Merged

fix(sanitizer): close case-insensitive scheme and scheme-relative URL bypasses in exfiltration guard#6518
bug-ops merged 2 commits into
mainfrom
fix/6508-exfil-scheme-bypass

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • ExfiltrationGuard::scan_output's image-URL detection (MARKDOWN_IMAGE_RE, REFERENCE_DEF_RE, HTML_IMG_RE, is_external_url) anchored on a literal lowercase https?:// scheme and required an explicit scheme prefix. An uppercase/mixed-case scheme (HTTPS://evil.com/pixel.gif) or a scheme-relative destination (//evil.com/pixel.gif) — both valid CommonMark/HTML5 and rendered identically to https:// by browsers — bypassed detection entirely.
  • A backslash-escaped quote inside a CommonMark image title (![t](https://evil.com/x.gif "a\"b")) also broke the title regex added in fix(sanitizer): close exfiltration guard markdown/HTML image regex bypasses #6507.
  • Fixed by making scheme matching case-insensitive throughout, treating an optional scheme-relative // prefix as equivalent to an explicit https?://, and tolerating \" as a non-terminating escaped quote in the double-quoted title branch.
  • Adversarial review found a sibling gap in URL_EXTRACT_RE (the tool-URL-injection flagging path) with the same case-sensitivity issue — folded that casing fix into this PR as well (trivial, same defect class). Scheme-relative support for that specific path is deferred as a larger design change (exact-string flagged-URL-set semantics) and marked with a TODO(critic) comment; a follow-up issue will be filed separately.
  • Ten new regression tests cover uppercase/mixed-case schemes, scheme-relative destinations across all three regex sites, the escaped-quote title, and negative cases (plain relative paths, interior non-leading //).

Closes #6508

Test plan

  • cargo +nightly fmt --check — clean
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings — clean
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins — 14713 passed, 35 skipped, 0 failed
  • cargo nextest run --config-file .github/nextest.toml -p zeph-sanitizer — 381 passed (10 new), 0 failed
  • RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler" — clean
  • gitleaks protect --staged — no leaks
  • Adversarial critique (rust-critic) and code review (rust-code-reviewer) both completed; review status: approved

@github-actions github-actions Bot added documentation Improvements or additions to documentation rust Rust code changes bug Something isn't working size/L Large PR (201-500 lines) labels Jul 19, 2026
bug-ops added 2 commits July 19, 2026 23:30
… bypasses in exfiltration guard

ExfiltrationGuard::scan_output's image-URL detection anchored on a literal
lowercase https?:// scheme and required an explicit scheme, so uppercase/
mixed-case schemes (HTTPS://evil.com/...) and scheme-relative destinations
(//evil.com/...) - both valid and renderer-equivalent to https:// - bypassed
detection silently. A backslash-escaped quote in a CommonMark image title
also broke the title regex added in #6507.

Make scheme matching case-insensitive across all three detection regexes
and is_external_url, and treat an optional scheme-relative // prefix as
equivalent to an explicit https?://. Tolerate \" as a non-terminating
escaped quote in the double-quoted title branch. Fold in the same casing
fix for the sibling URL_EXTRACT_RE (tool-URL-injection flagging path);
scheme-relative support there is deferred as a larger design change
(exact-string flagged-URL-set semantics), marked with a TODO comment.

Closes #6508
@bug-ops
bug-ops force-pushed the fix/6508-exfil-scheme-bypass branch from 8408240 to 2298f7f Compare July 19, 2026 21:30
@bug-ops
bug-ops enabled auto-merge (squash) July 19, 2026 21:30
@bug-ops
bug-ops merged commit 81926e6 into main Jul 19, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6508-exfil-scheme-bypass branch July 19, 2026 21:39
bug-ops added a commit that referenced this pull request Jul 19, 2026
… tool-URL flagging path (#6523)

URL_EXTRACT_RE required a literal https?: scheme, so a scheme-relative URL
(//evil.com/x) in untrusted tool output was never added to the per-turn
flagged_urls set and could never be flagged in a later tool call, unlike
is_external_url which already treats scheme-relative URLs as external
(fixed for scan_output in #6508/#6518).

Widening the regex alone would open a second gap: flagged_urls does
exact-string matching, so the same origin captured in one textual form
would not match its occurrence in the other. URL_EXTRACT_RE now matches
an optional scheme, and a new normalize_url_for_matching helper
canonicalizes to scheme-relative form for flagged_urls set membership.
Normalization is applied only at the flagged_urls-specific build site
(sanitize.rs) and at lookup time, keeping extract_flagged_urls itself
raw so its other consumer (user_provided_urls, feeding the default-on
UrlGroundingVerifier) is unaffected.

Closes #6519
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Rust code changes size/L Large PR (201-500 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

exfiltration guard misses uppercase-scheme and scheme-relative image URLs

1 participant