.forge/issues/7b0b0eae-93d2-4cb3-8fa5-e01cbf1447ac/issue.md 1.9 KiB 41 lines Raw

id: 7b0b0eae-93d2-4cb3-8fa5-e01cbf1447ac title: 'Markdown link/image URLs are not scheme-filtered (javascript: XSS)' state: closed labels:


Markdown link and image destinations are emitted verbatim, so [click](javascript:...) renders as a live javascript: link on the mirror.

Verified with a committed README:

[click me](javascript:alert(document.domain))
![img](javascript:alert(2))
$ grep -o '<a href="[^"]*"' out/index.html
<a href="javascript:alert(document.domain)"
$ grep -o '<img src="[^"]*"' out/index.html
<img src="javascript:alert(2)"

markdown_to_html intercepts Event::Html / Event::InlineHtml and escapes them (good — raw <script> in markdown is neutralized), but Tag::Link { dest_url } / Tag::Image { dest_url } pass through untouched. pulldown-cmark does not sanitize URLs by design.

Impact: stored XSS one click away from README, wiki pages, issue/PR bodies and release notes — same origin as /inbox/*, so any injected script can post submissions with the visitor's IP. Complements #61 (which needs no click) — fixing both closes the pair.

Fix: keep intercepting events in markdown_to_html and add a Tag arm that rewrites Link/Image destinations through an allowlist:

Acceptance:

view raw