#61 Stored XSS: raw/<path> serves committed HTML/SVG as text/html

closedsecurityopened
details
updated
id
4ab82678-f954-4bef-bfbc-eed37ab38bde

dist/raw/<path> is byte-identical repo content served with a content type chosen from the extension, so any committed .html (or .svg, .xhtml, .xml) executes script on the site's origin.

Verified:

$ printf '<!doctype html><script>fetch("/inbox/issue",{method:"POST"})</script>hi\n' > evil.html
$ hu13 build . -o out && hu13 serve --www out --inbox-dir /tmp/ib
$ curl -D- http://127.0.0.1:39501/raw/evil.html
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
<!doctype html><script>fetch("/inbox/issue",{method:"POST"})</script>hi

files/evil.html.html is correctly escaped (&lt;script&gt;), so the escaped view is fine — the raw view is the hole. It needs no click: visiting the URL runs it.

Why it matters here specifically: the page is same-origin with /inbox/*, so injected script satisfies the Origin gate, can POST spam from the visitor's own IP, has full access to the DOM of the mirror (phishing/defacement), and can chain the inbox forms. It also breaks the project invariant "bodies from the repo are treated as untrusted".

Same hole exists on Cloudflare Pages (it also types by extension), so this cannot be fixed in serve alone.

Fix direction:

Acceptance:

Comments (1)

all issues