diff --git a/.changeset/smooth-baboons-move.md b/.changeset/smooth-baboons-move.md new file mode 100644 index 000000000000..be1e4185cfd2 --- /dev/null +++ b/.changeset/smooth-baboons-move.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes a bug where inline styles and scripts didn't work when CSP was enabled. Now when adding `` elements inside an Astro component, their hashes care correctly computed. diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index 9ad64fed0c27..ea7c132e2328 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -35,6 +35,7 @@ import { type Pipeline, Slots, getParams, getProps } from './render/index.js'; import { isRoute404or500, isRouteExternalRedirect, isRouteServerIsland } from './routing/match.js'; import { copyRequest, getOriginPathname, setOriginPathname } from './routing/rewrite.js'; import { AstroSession } from './session.js'; +import { generateCspDigest } from './encryption.js'; export const apiContextRoutesSymbol = Symbol.for('context.routes'); /** @@ -435,6 +436,21 @@ export class RenderContext { const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline; const { links, scripts, styles } = await pipeline.headElements(routeData); + + const extraStyleHashes = []; + const extraScriptHashes = []; + const shouldInjectCspMetaTags = !!manifest.csp; + const cspAlgorithm = manifest.csp?.algorithm ?? 'SHA-256'; + if (shouldInjectCspMetaTags) { + for (const style of styles) { + extraStyleHashes.push(await generateCspDigest(style.children, cspAlgorithm)); + } + + for (const script of scripts) { + extraScriptHashes.push(await generateCspDigest(script.children, cspAlgorithm)); + } + } + const componentMetadata = (await pipeline.componentMetadata(routeData)) ?? manifest.componentMetadata; const headers = new Headers({ 'Content-Type': 'text/html' }); @@ -492,13 +508,13 @@ export class RenderContext { hasRenderedServerIslandRuntime: false, headInTree: false, extraHead: [], - extraStyleHashes: [], - extraScriptHashes: [], + extraStyleHashes, + extraScriptHashes, propagators: new Set(), }, cspDestination: manifest.csp?.cspDestination ?? (routeData.prerender ? 'meta' : 'header'), - shouldInjectCspMetaTags: !!manifest.csp, - cspAlgorithm: manifest.csp?.algorithm ?? 'SHA-256', + shouldInjectCspMetaTags, + cspAlgorithm, // The following arrays must be cloned, otherwise they become mutable across routes. scriptHashes: manifest.csp?.scriptHashes ? [...manifest.csp.scriptHashes] : [], scriptResources: manifest.csp?.scriptResources ? [...manifest.csp.scriptResources] : [], diff --git a/packages/astro/test/csp.test.js b/packages/astro/test/csp.test.js index c79740aace81..13e0810ed88c 100644 --- a/packages/astro/test/csp.test.js +++ b/packages/astro/test/csp.test.js @@ -286,6 +286,24 @@ describe('CSP', () => { assert.equal(meta.attr('content'), undefined, 'meta tag should not be present'); }); + it('should generate hashes for inline styles', async () => { + fixture = await loadFixture({ + root: './fixtures/csp/', + }); + await fixture.build(); + const html = await fixture.readFile('/inline/index.html'); + const $ = cheerio.load(html); + + const meta = $('meta[http-equiv="Content-Security-Policy"]'); + // hash of the +

Powered By

+ diff --git a/packages/astro/test/fixtures/csp/src/pages/inline.astro b/packages/astro/test/fixtures/csp/src/pages/inline.astro new file mode 100644 index 000000000000..05443a27ac4e --- /dev/null +++ b/packages/astro/test/fixtures/csp/src/pages/inline.astro @@ -0,0 +1,5 @@ +--- +import InlineStuff from "../components/InlineStuff.astro"; +--- + +