Files
pad/internal/server
xarmian 7429de3933 fix(oauth): CSP nonce on consent screen so the inline UI-state script can run (#383)
Pasting the bare https://mcp.getpad.dev URL into Claude Desktop now
reaches pad's consent screen, but the Allow button stays disabled
even when the user picks workspaces. Cause: the consent template
ships UI-state JS in an inline <script> block (workspace selection
flips disabled=false on the Allow button + handles the wildcard
mutual-exclusion warning), but pad's strict response CSP is
"script-src 'self'" with no 'unsafe-inline' and no nonce — so the
browser silently blocks the inline script and the Allow button
stays at its initial disabled=true.

Adopts the same nonce + strict-dynamic CSP pattern pad already uses
for the SvelteKit SPA bootstrap (see server.go's setupRouter SPA
route): renderConsent generates a per-request nonce via
generateCSPNonce, sets a CSP header that authorizes that nonce
("script-src 'self' 'nonce-X' 'strict-dynamic'") before writing
the body, and threads the same nonce into the template's <script>
tag's nonce attribute.

This is per-handler (overrides SecurityHeaders middleware on the
consent response only); every other endpoint keeps the strict
no-nonce baseline. matches the existing SPA-bootstrap nonce path
exactly so future security-hardening on either side stays
self-consistent.

Adds TestOAuth_ConsentScreen_NonceCSPLetsInlineScriptRun pinning
two facts:
  1. CSP on the consent response carries a 'nonce-...' token in
     script-src (proves the override fired and we didn't fall back
     to the strict baseline).
  2. The body's <script> tag carries the SAME nonce value (proves
     the two are linked — drift would re-introduce the bug).
Both are necessary; either failing causes the browser to block.
2026-05-02 19:21:25 -04:00
..