Skip to content

Harden local mode against script injection and cross-origin file reads - #68

Merged
patakuti merged 6 commits into
mainfrom
security-hardening
Oct 2, 2026
Merged

patakuti merged 6 commits into
mainfrom
security-hardening

Conversation

@patakuti

@patakuti patakuti commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Problem

In local mode /local/<absolute path> serves any readable file without authentication. Two paths let untrusted content reach it from the user's browser:

  • Raw HTML in a rendered Markdown file (goldmark WithUnsafe) was passed through, so a <script> in e.g. a remote README ran on the proxy origin and could fetch('/local/...').
  • Non-Markdown files were served as-is with their own Content-Type, so a remote .html / .svg ran script on the proxy origin too.
  • No Host / Origin validation, so DNS rebinding or other sites could reach the server.

Changes

  • Sanitize rendered HTML with a bluemonday allowlist (markdown.Render = convert → rewrite links → sanitize). Tables, images, <details>, highlighting, line anchors, math and inline SVG are preserved; scripts, iframes, on*, javascript: and SVG script/foreignObject/use are removed.
  • Host check (local mode): only localhost, 127.0.0.1, [::1].
  • Origin / Fetch Metadata check: reject cross-origin cors requests and mismatched Origin (local mode). No CORS headers are sent.
  • CSP with a per-request nonce (script-src 'nonce-…', connect-src 'self', frame-ancestors 'none', …). Inline event handlers and javascript: links in templates were replaced with addEventListener.
  • Files served as-is: nosniff, plus Content-Security-Policy: sandbox allow-scripts for HTML/SVG/XML so they run in an opaque origin.
  • CDN: Mermaid pinned to 10.9.8, KaTeX 0.16.11, both with SRI.
  • Test confirming Authorization is not forwarded on cross-host redirects.
  • README Security section updated.

/local/ is intentionally not restricted (viewing logs under e.g. ~/.claude/ is a primary use case); the above blocks the routes to it instead.

Behavior changes

  • Local .html files are sandboxed (scripts run, but no localStorage/cookies).
  • <style> tags and style attributes other than width/height in Markdown are removed.

Testing

  • go test ./... and make build pass (new tests for sanitizing, Host/Origin, CSP, passthrough, SRI, redirect credentials).
  • Manually verified in Chrome with a malicious Markdown/HTML/SVG: no script execution or file leak; theme switch, TOC, copy button, Live Reload, Mermaid, KaTeX, directory and top pages work.

🤖 Generated with Claude Code

patakuti and others added 6 commits October 2, 2026 20:54
Raw HTML was passed through (goldmark WithUnsafe), so scripts in a
rendered document ran on the proxy origin and could fetch /local/ files.
Add markdown.Render (convert, rewrite links, sanitize) and use it in all
handlers. Tables, images, details, highlighting, line anchors and inline
SVG are preserved.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@patakuti
patakuti merged commit d38d3cf into main Oct 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant