Finding
findRemoteReferences() in scripts/lib/svg-active-content.mjs — the gate that keeps a visitor's IP, User-Agent and Referer from reaching a host an SVG's author chose (per the validator's own threat note in scripts/validate-architecture-assets.mjs:262-269) — scans SVG style-block CSS raw. Attribute values get entity-decoding and whitespace stripping (normalizeUri, :458), but CSS text passed to cssTargets() (:405) gets neither comment removal nor escape decoding, and CSS_URL_PATTERN/CSS_IMPORT_PATTERN only match the unobfuscated forms.
Per CSS Syntax Level 3, comments are removed before tokenization and escapes in url tokens are decoded, so a browser fetches all of these — confirmed live against main @ 8afaa67:
Steps to Reproduce / Evidence
node --input-type=module -e "
import { findRemoteReferences } from './scripts/lib/svg-active-content.mjs';
findRemoteReferences('<svg><style>.a{background:url( /**/ \"https://evil.example/x\" )}</style></svg>') // => [] MISSED
findRemoteReferences('<svg><style>@import /**/ \"https://evil.example/x\";</style></svg>') // => [] MISSED
findRemoteReferences('<svg><style>.a{background:url(\\68 ttps://evil.example/x)}</style></svg>') // => [] MISSED (\\68 = 'h')
"
The unobfuscated baseline url("https://evil.example/x") is detected, so this is a gap in normalization, not in the reporting path.
This is the residual bypass class of the recently landed hardening series (#1039, #1044, #1056, #1063, #1068): each closed one parser-divergence between the scanner and a real CSS/XML parser; comment and escape handling inside CSS values is the divergence still open.
Recommendation
Normalize CSS before pattern matching in cssTargets(): strip /* ... */ comments (careful: not inside strings), and decode CSS escapes (\\XX hex plus the simple \\<char> form) in url tokens before testing with remoteTarget(). Add regression tests for the three cases above. One deliverable — a single PR against scripts/lib/svg-active-content.mjs plus tests/svg-active-content.test.mjs closes it.
Filed by scanner agent (ACMM L4 — issues-only mode)
🐝 Hive Agent: scanner | Instance: hosted-available-lke648397-260827-5n31 | SHA: unknown
— hive: agent=scanner backend=copilot model=kimi-k3 copilot=1.0.88
Finding
findRemoteReferences()inscripts/lib/svg-active-content.mjs— the gate that keeps a visitor's IP, User-Agent and Referer from reaching a host an SVG's author chose (per the validator's own threat note inscripts/validate-architecture-assets.mjs:262-269) — scans SVG style-block CSS raw. Attribute values get entity-decoding and whitespace stripping (normalizeUri, :458), but CSS text passed tocssTargets()(:405) gets neither comment removal nor escape decoding, andCSS_URL_PATTERN/CSS_IMPORT_PATTERNonly match the unobfuscated forms.Per CSS Syntax Level 3, comments are removed before tokenization and escapes in url tokens are decoded, so a browser fetches all of these — confirmed live against
main@ 8afaa67:Steps to Reproduce / Evidence
The unobfuscated baseline
url("https://evil.example/x")is detected, so this is a gap in normalization, not in the reporting path.This is the residual bypass class of the recently landed hardening series (#1039, #1044, #1056, #1063, #1068): each closed one parser-divergence between the scanner and a real CSS/XML parser; comment and escape handling inside CSS values is the divergence still open.
Recommendation
Normalize CSS before pattern matching in
cssTargets(): strip/* ... */comments (careful: not inside strings), and decode CSS escapes (\\XXhex plus the simple\\<char>form) in url tokens before testing withremoteTarget(). Add regression tests for the three cases above. One deliverable — a single PR againstscripts/lib/svg-active-content.mjsplustests/svg-active-content.test.mjscloses it.Filed by scanner agent (ACMM L4 — issues-only mode)
🐝 Hive Agent:
scanner| Instance:hosted-available-lke648397-260827-5n31| SHA:unknown— hive: agent=scanner backend=copilot model=kimi-k3 copilot=1.0.88