Skip to content

Security: optionally drop CSP 'unsafe-inline' via a nonce (post-sanitize hardening) #424

Description

@williamzujkowski

From the broad QA/security sweep (consensus, adversarially verified). These are structural to static GitHub Pages hosting (no server to send real HTTP response headers) + the meta-only CSP, so they need a decision rather than a one-line fix. (The inert X-Content-Type-Options/Permissions-Policy metas and the Referrer-Policy<meta name="referrer"> fix are handled in the QA-sweep PR; these remain.)

1. 'unsafe-inline' script-src + no HTML sanitization (HIGH)

script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' (BaseLayout) + the markdown rehype pipeline has no rehype-sanitize — raw HTML in posts renders verbatim. Real protection is currently 100% process (only the author writes posts). Options: add a rehype-sanitize allowlist pass (must not strip .flow/.arch/.zine-doodle markup), and/or drop 'unsafe-inline' by giving the one legit inline script (theme-flash snippet) a nonce/hash. Note the feed (feed.xml.ts) already sanitizes post HTML — the page render path doesn't, an inconsistent trust boundary.

2. Broken gist embeds — 7 posts (HIGH, user-facing)

7 posts contain live <script src="https://gist.github.com/…js"> embeds that the CSP blocks (gist.github.com isn't in script-src), so they don't render for readers. Decide: allowlist the github gist domains in CSP (relaxes policy, adds external script/style), or inline the code from the gists/ source-of-truth dir, or remove the dead <script> tags. Inlining is the most secure + fastest-loading.

3. No clickjacking protection (HIGH, structural)

CSP omits frame-ancestors, and frame-ancestors is ignored when CSP is delivered via <meta> (CSP L3 spec); X-Frame-Options has no meta form. So the site is iframe-embeddable anywhere. Only fixable by fronting GH Pages with an edge (Cloudflare/Netlify _headers) that can send real headers — a hosting decision.

Recommendation: #2 (inline gists) is a clear win; #1 (sanitize) is worth doing for defense-in-depth; #3 is accept-or-migrate-hosting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    maintenanceRoutine maintenance task

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions