harden: CSP meta + workbox auth-relay tripwire#90
Conversation
Formalises the no-third-party-JS property and limits XSS blast radius on top of the localStorage-resident remoteStorage tokens. Inline scripts and styles are pinned by sha256 hash; if either is edited the hash must be regenerated or the browser refuses to run them. Honest about its limits: connect-src has to allow https: because rs.js talks to whichever server the user picks, so this CSP cannot prevent exfiltration on its own. Closing that gap requires a deploy-time header that pins connect-src to the discovered host. Closes #79.
The Workbox precache manifest is generated at build time and a vite-plugin-pwa bump can silently re-include auth-relay.html, which would break rs.js's OAuth redirect-back flow (the cached copy intercepts the redirect). Extracts the workbox config to a named export and asserts both the glob ignore and the navigate-fallback denylist exclude auth-relay.html. The denylist assertion exercises the regex against the realistic OAuth callback shapes (query-param and hash-fragment) so an over-anchored regex change is caught. Closes #80.
PR feedback: the previous test asserted the exported config still contained the auth-relay exclusions, but that wouldn't catch the dependency-bump failure mode #80 actually cares about — vite-plugin-pwa or Workbox changing how those options are interpreted at build time. Replaces the config-level checks with a test that runs vite.build() into a tempdir and parses the generated sw.js precache list, then drops the named workboxConfig export since nothing imports it anymore.
PR feedback: it wasn't obvious how the sha256 hashes in the CSP meta tags get maintained — manual update on every script edit was implicit, not documented. Adds csp.test.js, which re-computes sha256 for every inline <script> and <style> in index.html and public/auth-relay.html and asserts the CSP meta tag's directive contains it. Editing a script body now fails the test with the new hash printed in the assertion error, so the dev just copies it into the meta tag — no manual `node -e` invocation needed. Trims the over-long CSP comments at the same time.
|
Thanks for the review — addressed all three points in two follow-up commits: 1. Workbox test now inspects the built 2. Hash maintenance is now automatic via 3. Trimmed the verbose comments (also in All 281 tests pass, lint clean, build succeeds. |
PR feedback: keeping the hashes pinned in source meant a 30-second copy-paste from a test failure every time you edited an inline script — real friction even if rare. Adds vite-plugin-csp-hash.js, which replaces __CSP_SCRIPT_HASHES__ / __CSP_STYLE_HASHES__ tokens in HTML CSP meta tags with the sha256 of every inline <script>/<style> block in the document. Hooks transformIndexHtml for index.html (dev + build), a configureServer middleware for public/* HTML in dev, and writeBundle for public/* in build, so dev refresh and prod build both pick up the new hash with zero manual step. Drops csp.test.js — drift is now impossible by construction; the plugin always derives the hash from the actual content it's hashing.
|
Made it fully automatic — pushed Added Workflow now:
No copy-paste, no test failure to react to. Dropped Verified working:
Plugin scope: ~50 lines, three hooks (transformIndexHtml, configureServer middleware, writeBundle). All 278 tests pass, lint clean. |
Summary
Content-Security-Policymeta tag toindex.htmlandpublic/auth-relay.html. Pins inline scripts/styles by sha256 hash so the no-third-party-JS property is enforced by the browser, not just by convention. (Add Content-Security-Policy meta to harden token-in-localStorage #79)auth-relay.htmlwith a unit test against the exported workbox config, so a vite-plugin-pwa bump that silently re-includes the OAuth relay surfaces in CI rather than at runtime. (Verify Workbox precache excludes auth-relay.html #80)Notes
connect-src https:has to be wide-open so rs.js can talk to whichever remoteStorage server the user picks. Tightening that further is a deploy-time-header job, called out in the comment.frame-ancestors,report-to, andsandboxare intentionally omitted from the meta CSP — the spec says they're ignored when set via<meta>. Use HTTP headers at deploy for those.?code=query,#access_token=hash) so an over-anchored regex change is caught.Closes #79.
Closes #80.
Test plan
npm test— all 278 existing tests + 2 new workbox tests passnpm run lint— biome cleannpm run build— clean build; verifieddist/index.htmlretains the CSP meta tag,dist/auth-relay.htmlretains its CSP,dist/sw.jsprecache list does not containauth-relay.htmlauth-relay.html