diff --git a/.changeset/two-zebras-check.md b/.changeset/two-zebras-check.md new file mode 100644 index 000000000000..efb6ecb20e6d --- /dev/null +++ b/.changeset/two-zebras-check.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/adapter-cloudflare': minor +'@sveltejs/kit': minor +--- + +Enable adapter-cloudflare to specify Content-Security-Policy in \_headers file diff --git a/packages/adapter-cloudflare/index.js b/packages/adapter-cloudflare/index.js index 32f0e098509b..37008f45fa44 100644 --- a/packages/adapter-cloudflare/index.js +++ b/packages/adapter-cloudflare/index.js @@ -50,7 +50,7 @@ export default function (options = {}) { JSON.stringify(get_routes_json(builder, written_files, options.routes ?? {}), null, '\t') ); - writeFileSync(`${dest}/_headers`, generate_headers(builder.getAppPath()), { flag: 'a' }); + writeFileSync(`${dest}/_headers`, generate_headers(builder), { flag: 'a' }); if (builder.prerendered.redirects.size > 0) { writeFileSync(`${dest}/_redirects`, generate_redirects(builder.prerendered.redirects), { @@ -159,18 +159,27 @@ function get_routes_json(builder, assets, { include = ['/*'], exclude = ['' }; } -/** @param {string} app_dir */ -function generate_headers(app_dir) { - return ` -# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS === +/** @param {import('@sveltejs/kit').Builder} builder */ +function generate_headers(builder) { + const app_dir = builder.getAppPath(); + const csp_header = builder.generateCspHeaderValue(); + + let headers = '# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS ==='; + + if (csp_header) { + headers += `\n/*\n Content-Security-Policy: ${csp_header}`; + } + + headers += ` /${app_dir}/* X-Robots-Tag: noindex Cache-Control: no-cache /${app_dir}/immutable/* ! Cache-Control Cache-Control: public, immutable, max-age=31536000 -# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS === -`.trimEnd(); +# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS ===`; + + return headers; } /** @param {Map} redirects */ diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index e02efbdd1e28..8b708eab245d 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -12,6 +12,7 @@ import generate_fallback from '../postbuild/fallback.js'; import { write } from '../sync/utils.js'; import { list_files } from '../utils.js'; import { find_server_assets } from '../generate_manifest/find_server_assets.js'; +import { Csp } from '../../runtime/server/page/csp.js'; const pipe = promisify(pipeline); const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm']; @@ -192,6 +193,14 @@ export function create_builder({ }); }, + generateCspHeaderValue() { + if (!config.kit.csp.directives) return null; + + const csp = new Csp(config.kit.csp, { prerender: false }); + + return csp.csp_provider.get_header(false); + }, + getBuildDirectory(name) { return `${config.kit.outDir}/${name}`; }, diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index b4a837768420..24d8e80d6f30 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -134,6 +134,11 @@ export interface Builder { */ generateManifest(opts: { relativePath: string; routes?: RouteDefinition[] }): string; + /** + * Generate a Content Security Policy header string which can be used to define static header files for adapters like adapter-cloudflare + */ + generateCspHeaderValue(): string | null; + /** * Resolve a path to the `name` directory inside `outDir`, e.g. `/path/to/.svelte-kit/my-adapter`. * @param name path to the file, relative to the build directory diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 5c7db29c3301..aa4c07983aed 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -116,6 +116,11 @@ declare module '@sveltejs/kit' { */ generateManifest(opts: { relativePath: string; routes?: RouteDefinition[] }): string; + /** + * Generate a Content Security Policy header string which can be used to define static header files for adapters like adapter-cloudflare + */ + generateCspHeaderValue(): string | null; + /** * Resolve a path to the `name` directory inside `outDir`, e.g. `/path/to/.svelte-kit/my-adapter`. * @param name path to the file, relative to the build directory