Skip to content

feat(security): Content-Security-Policy in report-only mode - #48

Open
ogorzalka wants to merge 3 commits into
mainfrom
fix/security-headers
Open

ogorzalka wants to merge 3 commits into
mainfrom
fix/security-headers

Conversation

@ogorzalka

Copy link
Copy Markdown
Member

Why

Step 2 of .claude/plans/security-headers.md (Aikido: CSP not set). Report-only: nothing is blocked, violations show in the browser console.

What

  • vercel.json: a Content-Security-Policy-Report-Only header. Scripts by origin ('self', Google Tag Manager) and by hash for Starlight's five inline scripts; styles inline (Expressive Code writes style attributes); YouTube's nocookie embed in frames; Google Analytics 4 for images and connections; 'unsafe-eval' for Alpine.js, 'wasm-unsafe-eval' for Pagefind.
  • No inline script of ours is left: analytics and consent scripts are processed by Astro (GTM id in src/data/analytics.ts), the homepage terminal sequence is a JSON data block, the changelog filter's onsubmit is an addEventListener, assetsInlineLimit: 0 makes Astro emit scripts as files.
  • scripts/check-csp.mjs (postbuild) fails when an inline script is not covered by a hash of the policy or a page has an inline event handler, so a Starlight upgrade cannot break the site silently.

Astro's own security.csp was tried and dropped: it hashes styles too, which makes style attributes (398 on a doc page) impossible, and it is a <meta> tag, so never report-only.

Next

Browse the preview with the console open, fix what it reports, then a last PR turns the header into an enforced Content-Security-Policy.

The policy allows scripts by origin (self, Google Tag Manager) and by hash for the five inline scripts Starlight writes in every page, styles inline (Expressive Code writes style attributes hashes cannot cover), YouTube's nocookie embed in frames and Google Analytics 4 for images and connections. 'unsafe-eval' is there for Alpine.js, 'wasm-unsafe-eval' for Pagefind.

The site's own scripts no longer sit inline in the page: the analytics and consent scripts are processed by Astro (the GTM id moves to src/data/analytics.ts), the homepage's terminal sequence becomes a JSON data block, the changelog filter's inline onsubmit an addEventListener, and assetsInlineLimit is 0 so Astro emits scripts as files.

scripts/check-csp.mjs runs after the build and fails when an inline script is not covered by a hash of the policy, or when a page has an inline event handler: a Starlight upgrade would otherwise break the site without a word.
@vercel

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
pollora.dev Ready Ready Preview Oct 19, 2026 4:09pm UTC

The homepage video asks for its thumbnail on i.ytimg.com: add it to img-src. CLAUDE.md now says where the headers live, how to allow a new service, what check-csp guards and which concessions the policy makes.

This branch was successfully deployed

1 active deployment
Preview — 460cba99 Deployed Oct 19, 2026 by vercel[bot]
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