Skip to content

fix(sanitizer): close exfiltration guard markdown/HTML image regex bypasses#6507

Merged
bug-ops merged 1 commit into
mainfrom
fix/6476-exfil-image-regex-bypass
Jul 19, 2026
Merged

fix(sanitizer): close exfiltration guard markdown/HTML image regex bypasses#6507
bug-ops merged 1 commit into
mainfrom
fix/6476-exfil-image-regex-bypass

Conversation

@bug-ops

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

Copy link
Copy Markdown
Owner

Summary

  • ExfiltrationGuard::scan_output's markdown/HTML image detection regexes only matched a narrow literal syntax, missing three CommonMark/HTML5-valid variants real renderers still treat as images: leading whitespace or angle-bracket-wrapped link destinations, and unquoted HTML <img src=...> attribute values.
  • Loosened MARKDOWN_IMAGE_RE, REFERENCE_DEF_RE, and HTML_IMG_RE to cover all three variants via regex alternation, updating capture-group consumption at every call site in scan_output.
  • Adversarial review (impl-critic) caught a regression introduced while tightening the bare-URL match: narrowing it to stop before a whitespace-wrapped destination broke detection of standard CommonMark image titles (![t](https://evil.com/x.gif "title")). Fixed by adding an explicit optional title clause (double-quoted, single-quoted, or paren-wrapped).
  • Added 9 regression tests asserting scan_output() actually blocks/substitutes each bypass variant (not just that the regex matches), covering the three original bypasses, the title-syntax fix, and combinations of the two.

Two minor, pre-existing gaps outside this PR's scope were flagged during review as follow-up candidates: case-sensitive scheme matching (HTTPS:// bypasses both the regex and is_external_url) and scheme-relative URLs (//evil.com/x.gif bypasses all https?://-anchored patterns). Will file a separate issue for these.

Closes #6476

Test plan

  • cargo nextest run -p zeph-sanitizer — 366 passed, 2 skipped
  • 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 — 14644 passed, 35 skipped
  • 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-banner --redact — no leaks

@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
bug-ops enabled auto-merge (squash) July 19, 2026 19:30
@bug-ops
bug-ops force-pushed the fix/6476-exfil-image-regex-bypass branch from 89c9eef to b61752b Compare July 19, 2026 19:31
…passes

ExfiltrationGuard::scan_output only matched a narrow literal syntax for
markdown/HTML images, missing three CommonMark/HTML5-valid variants that
real renderers still treat as images: leading whitespace or angle-bracket
wrapping in the link destination, and unquoted HTML img src attributes.
Loosen MARKDOWN_IMAGE_RE, REFERENCE_DEF_RE, and HTML_IMG_RE to cover all
three, threading the correct alternate capture group through at every
call site, and add regression tests for each variant.

Also fixes a regression introduced while tightening the bare-URL match:
narrowing it to stop before a whitespace-wrapped destination broke
detection of standard CommonMark image titles, closed by adding an
explicit optional title clause to the regex.

Closes #6476
@bug-ops
bug-ops force-pushed the fix/6476-exfil-image-regex-bypass branch from b61752b to 2c161dc Compare July 19, 2026 19:34
@bug-ops
bug-ops merged commit 6cf9ea8 into main Jul 19, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/6476-exfil-image-regex-bypass branch July 19, 2026 19:43
bug-ops added a commit that referenced this pull request Jul 19, 2026
… 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 added a commit that referenced this pull request Jul 19, 2026
… bypasses in exfiltration guard (#6518)

* fix(sanitizer): close case-insensitive scheme and scheme-relative URL 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

* docs(sanitizer): link URL_EXTRACT_RE TODO to filed follow-up issue #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.

High: exfiltration guard's markdown/HTML image regexes bypassable via valid CommonMark and HTML5 syntax

1 participant