Hand-shipped project icons may embed data:image rasters - #207
Conversation
The active-content gate added alongside the icon generator (PR 195) refused every non-#fragment href, which silently broke previously working project-icon.svg files that wrap a raster in an SVG <image> with a base64 data: URI. Projects only appear in the sidebar once trusted, so an inline data:image/* href in a repo's own icon is the project embedding its own artwork, not a foreign fetch — allow it in the on-disk gate (hrefs only; CSS url() and every other data: payload stay refused). Generated icons keep the original strict rule via a new usable_generated_icon_bytes gate: pure vector art, no data: hrefs of any kind, and the design brief now says so explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BwbGQzyQQX3Gpj63DmX4W
|
@claude review |
|
Claude finished @ghackett's task in 2m 33s —— View job Claude finished the review
|
Review caught that keying on the data:image/ prefix also admitted image/svg+xml — XML whose base64 payload could smuggle exactly the script/handler content the plain-text checks can't see. The on-disk gate now accepts only png/jpeg/gif/webp/bmp/x-icon (with the subtype terminated by ; or ,), and tests pin svg+xml, lookalike subtypes, and percent-encoded variants as refused. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BwbGQzyQQX3Gpj63DmX4W
|
Addressed the |
Tighten the raster allowlist to the one format the motivating icons actually use; jpeg/gif/webp/bmp/x-icon stay off the list until an icon needs one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BwbGQzyQQX3Gpj63DmX4W

What
Restores the icons the sidebar stopped showing for repos whose
project-icon.svgwraps a raster in an SVG<image>with a base64data:URI (e.g. podcast-hacker and standup-timer). The active-content gate introduced with the icon generator in #195 refused everyhrefthat wasn't a local#fragment, which silently rejected those previously working files.The rule change
usable_icon_bytes): anhref/xlink:hrefmay now also carry an inlinedata:image/pngURI. Projects only appear in the sidebar once they're trusted, so an embedded raster in a repo's own icon is the project shipping its own artwork, not a foreign fetch. The carve-out is deliberately narrow: only thehrefbranch, only PNG (subtype terminated by;or,) —data:image/svg+xmlstays refused (it's XML whose base64 payload could smuggle the script/handler content the plain-text checks can't see), other raster codecs stay off the list until an icon actually needs one, and CSSurl(data:...), every otherdata:media type, data URIs hidden behind leading whitespace, scripts, event handlers,@import, and all external URLs remain refused as before.projecticons.usable_generated_icon_byteskeeps the original strict rule — everything above plus nodata:hrefs at all.icongen.extract_svgnow vets replies with it, and the design brief gains an explicit "pure vector art only: no data: URIs, no embedded raster images" hard requirement, so what the gate enforces is also what the model is asked for.Tests
test_projecticons: inlinedata:image/pnghrefs (bothhrefandxlink:href) are accepted;data:image/svg+xml(base64 and percent-encoded), other raster codecs (jpeg,webp), lookalike subtypes (image/png-evil),data:text/html, whitespace-prefixed data URIs, andurl(data:...)are refused; all the existing active-content rejections still hold.test_icongen:extract_svgrefuses everydata:href — including the exactdata:image/pngpayload the on-disk gate now accepts, pinning the two gates' divergence.Verified against the real files: both repos' PNG-wrapped icons pass the on-disk gate and would still be refused if a generation run produced them. Full local suite: 36 passed.
🤖 Generated with Claude Code
https://claude.ai/code/session_012BwbGQzyQQX3Gpj63DmX4W