Files
pc_builder/config/shield.ts
T
Kevin L'Hereec 25d6222df0
Build & Deploy / build (push) Failing after 3s
Build & Deploy / docker (push) Skipped
fix: add worker-src to CSP to fix broken HMR connection
Without an explicit worker-src directive, browsers fall back to
script-src ('self' only) for Web Workers, which blocks the blob: worker
spawned internally by Vite's HMR client / Tuyau's client. This caused the
dev HMR websocket connection to drop intermittently ('server connection
lost. Polling for restart...'), leaving the Vue app in an inconsistent
state after a failed hot reload — components using the Link component
(internally named TuyauLink, from @tuyau/core) would throw 'You must
wrap your app in a TuyauProvider' because the provide/inject context set
up at the app root was lost.

Added worker-src 'self' blob: to config/shield.ts. Verified: dev server
restarts cleanly with the HMR socket connecting without CSP violations,
full test suite (30/30) still passes.
2026-07-23 15:30:01 +02:00

125 lines
3.4 KiB
TypeScript

import { defineConfig } from '@adonisjs/shield'
import app from '@adonisjs/core/services/app'
/**
* In development, Vite's dev server assets (scripts, styles, /@vite/client)
* are proxied through AdonisJS on the same origin, so 'self' already covers
* them — there is no working "@viteDevUrl"/"@viteHmrUrl" CSP keyword in this
* version of @adonisjs/shield (only "@viteUrl" exists, and it resolves to an
* empty string here since assetsUrl is a relative path, not an absolute
* dev-server URL). The one thing that truly lives on a different origin is
* the Vite HMR websocket, which runs on its own port (see VITE_HMR_PORT,
* default 24678) and therefore needs an explicit connect-src entry.
*/
const viteHmrPort = process.env.VITE_HMR_PORT ?? '24678'
const devConnectSrc = app.inDev ? [`ws://localhost:${viteHmrPort}`] : []
const shieldConfig = defineConfig({
/**
* Configure CSP policies for your app. Refer documentation
* to learn more.
*/
csp: {
/**
* Enable the Content-Security-Policy header.
*/
enabled: true,
/**
* Per-resource CSP directives.
*/
directives: {
defaultSrc: [`'self'`],
connectSrc: [`'self'`, ...devConnectSrc],
scriptSrc: [`'self'`],
// Some client-side dependencies (e.g. Vite's HMR client / the Tuyau
// API client) spawn a Web Worker from a `blob:` URL built at
// runtime from already same-origin-loaded code. Without an explicit
// worker-src, browsers fall back to script-src ('self' only), which
// blocks the blob: worker outright — this was observed to break the
// dev HMR connection and leave the Vue app in an inconsistent state
// (lost TuyauProvider context after a failed hot reload).
workerSrc: [`'self'`, 'blob:'],
styleSrc: [`'self'`, `'unsafe-inline'`, 'https://fonts.googleapis.com'],
imgSrc: [`'self'`, 'data:', 'https://fr.pc-builder.io', 'https://www.google.com'],
fontSrc: [`'self'`, 'data:', 'https://fonts.gstatic.com'],
},
/**
* Report violations without blocking resources.
*/
reportOnly: false,
},
/**
* Configure CSRF protection options. Refer documentation
* to learn more.
*/
csrf: {
/**
* Enable CSRF token verification for state-changing requests.
*/
enabled: true,
/**
* Route patterns to exclude from CSRF checks.
* Useful for external webhooks or API endpoints.
*/
exceptRoutes: [],
/**
* Expose an encrypted XSRF-TOKEN cookie for frontend HTTP clients.
*/
enableXsrfCookie: true,
/**
* HTTP methods protected by CSRF validation.
*/
methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
},
/**
* Control how your website should be embedded inside
* iframes.
*/
xFrame: {
/**
* Enable the X-Frame-Options header.
*/
enabled: true,
/**
* Block all framing attempts. Default value is DENY.
*/
action: 'DENY',
},
/**
* Force browser to always use HTTPS.
*/
hsts: {
/**
* Enable the Strict-Transport-Security header.
*/
enabled: true,
/**
* HSTS policy duration remembered by browsers.
*/
maxAge: '180 days',
},
/**
* Disable browsers from sniffing content types and rely only
* on the response content-type header.
*/
contentTypeSniffing: {
/**
* Enable X-Content-Type-Options: nosniff.
*/
enabled: true,
},
})
export default shieldConfig