Document responses carried no CSP, X-Frame-Options, Referrer-Policy, or Permissions-Policy — leaving clickjacking and zero script-injection defense in depth (security audit T4). - svelte.config.js: enable kit.csp hash mode. SvelteKit hashes its own inline hydration scripts; the inline theme <script> in app.html isn't part of %sveltekit.head%, so its sha256 is pinned by hand (csp-config.js) and added to script-src alongside object-src 'none', base-uri 'self', frame-ancestors 'none'. Styles stay unconstrained (Svelte emits dynamic inline style= attrs). - hooks.server.ts: applySecurityHeaders() sets X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, X-Content-Type-Options: nosniff, and a Permissions-Policy locking down unused features, only when the response hasn't already set them. - src/csp-theme-hash.test.ts: drift guard against editing the theme script without updating THEME_SCRIPT_HASH. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36 lines
1.5 KiB
JavaScript
36 lines
1.5 KiB
JavaScript
import adapter from '@sveltejs/adapter-node';
|
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
import { THEME_SCRIPT_HASH } from './csp-config.js';
|
|
|
|
/** @type {import('@sveltejs/kit').Config} */
|
|
const config = {
|
|
preprocess: vitePreprocess(),
|
|
kit: {
|
|
adapter: adapter({ out: 'build' }),
|
|
// Content-Security-Policy. `mode: 'hash'` makes SvelteKit hash the
|
|
// inline scripts IT injects (the hydration bootstrap) and append those
|
|
// hashes to `script-src`. It does NOT hash the theme <script> in
|
|
// app.html — that template script isn't part of `%sveltekit.head%`, so
|
|
// we pin its sha256 here by hand (THEME_SCRIPT_HASH). If that script is
|
|
// edited, the hash drifts and the theme-flash guard would be silently
|
|
// CSP-blocked; `svelte.config.test.js` recomputes the hash from
|
|
// app.html and fails if it no longer matches this constant.
|
|
// Styles are left unconstrained (Svelte emits dynamic inline `style=`
|
|
// attributes); this policy is clickjacking + script-injection defense
|
|
// in depth, not a full lockdown. The non-CSP defense-in-depth headers
|
|
// (X-Frame-Options, Referrer-Policy, Permissions-Policy) live in
|
|
// hooks.server.ts.
|
|
csp: {
|
|
mode: 'hash',
|
|
directives: {
|
|
'script-src': ['self', THEME_SCRIPT_HASH],
|
|
'object-src': ['none'],
|
|
'base-uri': ['self'],
|
|
'frame-ancestors': ['none']
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
export default config;
|