Astro 7 moved `csp` out of `experimental` — but to `security.csp`, not to the top level, and I put it at the top level. Astro's config schema drops keys it does not know without a word: the build stayed green, the types stayed green, and every response went out with no Content-Security-Policy at all. Only looking at a live response header showed it. Fixed, and now guarded: tests/config.test.ts reads the config and asserts the policy sits where Astro actually reads it. Checked that the test goes red against the broken config before trusting it green against this one. Verified on a running instance: the header is back, with the same per-script hashes as before.
This commit is contained in:
1 parent
61e9f4de5a
commit
dd8061d838
2 files changed
+34
-2
No files matched your search
+7
-2
@@ -10,11 +10,15 @@ export default defineConfig({
|
|||||||
// CSRF: handled by our own SameSite=Lax session cookie. Astro's checkOrigin
|
// CSRF: handled by our own SameSite=Lax session cookie. Astro's checkOrigin
|
||||||
// breaks multipart uploads behind a reverse proxy, because the proxy speaks
|
// breaks multipart uploads behind a reverse proxy, because the proxy speaks
|
||||||
// http to the app while the browser spoke https and the origins no longer match.
|
// http to the app while the browser spoke https and the origins no longer match.
|
||||||
security: { checkOrigin: false },
|
//
|
||||||
// Content-Security-Policy with hashes instead of 'unsafe-inline': Astro computes
|
// Content-Security-Policy with hashes instead of 'unsafe-inline': Astro computes
|
||||||
// the hash of every inline script and style it emits, at build time. The remaining
|
// the hash of every inline script and style it emits, at build time. The remaining
|
||||||
// directives are set by the middleware (src/middleware.ts).
|
// directives are set by the middleware (src/middleware.ts).
|
||||||
// Graduated out of `experimental` in Astro 7.
|
// Astro 7 graduated this out of `experimental` and it lives under `security` now.
|
||||||
|
// Careful: a top-level `csp` key is swallowed without a warning and the site then
|
||||||
|
// runs with no policy at all.
|
||||||
|
security: {
|
||||||
|
checkOrigin: false,
|
||||||
csp: {
|
csp: {
|
||||||
directives: [
|
directives: [
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
@@ -27,6 +31,7 @@ export default defineConfig({
|
|||||||
"frame-ancestors 'none'",
|
"frame-ancestors 'none'",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
},
|
||||||
server: { host: true, port: Number(process.env.PORT) || 4321 },
|
server: { host: true, port: Number(process.env.PORT) || 4321 },
|
||||||
vite: {
|
vite: {
|
||||||
ssr: {
|
ssr: {
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import config from '../astro.config.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Astro's config schema ignores keys it does not know — silently, with no
|
||||||
|
* warning. That is how the Content-Security-Policy went missing once: Astro 7
|
||||||
|
* moved `csp` from `experimental` to `security`, a top-level `csp` key kept
|
||||||
|
* type-checking and building, and the deployed site served no policy at all.
|
||||||
|
* Nothing in the build output said so. This test does.
|
||||||
|
*/
|
||||||
|
test('The CSP lives where Astro 7 reads it', () => {
|
||||||
|
const security = (config as any).security;
|
||||||
|
assert.ok(security, 'astro.config.mjs has no `security` block');
|
||||||
|
assert.ok(security.csp, 'the CSP must sit at `security.csp` — a top-level `csp` is ignored');
|
||||||
|
assert.ok(Array.isArray(security.csp.directives), '`security.csp.directives` must be a list');
|
||||||
|
|
||||||
|
// The directives the middleware does not add itself.
|
||||||
|
for (const d of ["default-src 'self'", "object-src 'none'", "frame-ancestors 'none'", "base-uri 'self'", "form-action 'self'"]) {
|
||||||
|
assert.ok(security.csp.directives.includes(d), `missing directive: ${d}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('No stray top-level `csp` or `experimental.csp` key', () => {
|
||||||
|
assert.equal((config as any).csp, undefined, 'a top-level `csp` key is ignored by Astro 7');
|
||||||
|
assert.equal((config as any).experimental?.csp, undefined, '`experimental.csp` is gone in Astro 7');
|
||||||
|
});
|
||||||
Reference in new issue
Block a user