From d899e3cac4d79296cc2cb8bcec5e451b9039e363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Correa=20Casablanca?= Date: Tue, 17 Sep 2024 18:31:26 +0200 Subject: [PATCH] fix: treat index.html as special case for netlify Signed-off-by: Andres Correa Casablanca --- @kindspells/astro-shield/package.json | 2 +- @kindspells/astro-shield/src/netlify.mts | 14 +++- .../astro-shield/src/tests/netlify.test.mts | 71 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/@kindspells/astro-shield/package.json b/@kindspells/astro-shield/package.json index 6df89a4..5092932 100644 --- a/@kindspells/astro-shield/package.json +++ b/@kindspells/astro-shield/package.json @@ -1,6 +1,6 @@ { "name": "@kindspells/astro-shield", - "version": "1.5.1", + "version": "1.5.2", "description": "Astro integration to enhance your website's security with SubResource Integrity hashes, Content-Security-Policy headers, and other techniques.", "private": false, "type": "module", diff --git a/@kindspells/astro-shield/src/netlify.mts b/@kindspells/astro-shield/src/netlify.mts index 1f95462..f1735c5 100644 --- a/@kindspells/astro-shield/src/netlify.mts +++ b/@kindspells/astro-shield/src/netlify.mts @@ -3,6 +3,7 @@ import { readFile, writeFile } from 'node:fs/promises' import type { CSPDirectives, HashesCollection, + PerPageHashes, SecurityHeadersOptions, } from './types.mts' import { serialiseCspDirectives, setSrcDirective } from './headers.mts' @@ -251,9 +252,16 @@ export const buildNetlifyHeadersConfig = ( entries: [], } - for (const [page, hashes] of Array.from( - resourceHashes.perPageSriHashes, - ).sort()) { + const pagesToIterate: [string, PerPageHashes][] = [] + for (const [page, hashes] of resourceHashes.perPageSriHashes) { + if (page === 'index.html' || page.endsWith('/index.html')) { + pagesToIterate.push([page.slice(0, -10), hashes]) + } + pagesToIterate.push([page, hashes]) + } + pagesToIterate.sort() + + for (const [page, hashes] of pagesToIterate) { const pathEntries: (HeaderEntry | CommentEntry)[] = [] if (securityHeadersOptions.contentSecurityPolicy !== undefined) { diff --git a/@kindspells/astro-shield/src/tests/netlify.test.mts b/@kindspells/astro-shield/src/tests/netlify.test.mts index 931cd2b..2d327e9 100644 --- a/@kindspells/astro-shield/src/tests/netlify.test.mts +++ b/@kindspells/astro-shield/src/tests/netlify.test.mts @@ -235,6 +235,77 @@ describe('buildNetlifyHeadersConfig', () => { ], } satisfies NetlifyHeadersRawConfig) }) + + it('creates a "double entry" for "index.html" files', () => { + const config = buildNetlifyHeadersConfig( + { contentSecurityPolicy: {} }, + { + perPageSriHashes: new Map([ + [ + 'index.html', + { + scripts: new Set([ + 'sha256-071spvYLMvnwaR0H7M2dfK0enB0cGtydTbgJkdoWq7c=', + ]), + styles: new Set(), + }, + ], + [ + 'es/index.html', + { + scripts: new Set([ + 'sha256-071spvYLMvnwaR0H7M2dfK0enB0cGtydTbgJkdoWq7c=', + ]), + styles: new Set(), + }, + ], + [ + 'fakeindex.html', + { + scripts: new Set([ + 'sha256-071spvYLMvnwaR0H7M2dfK0enB0cGtydTbgJkdoWq7c=', + ]), + styles: new Set(), + }, + ], + ]), + }, + ) + + const testEntries = [ + { + headerName: 'content-security-policy', + value: + "script-src 'self' 'sha256-071spvYLMvnwaR0H7M2dfK0enB0cGtydTbgJkdoWq7c='; style-src 'none'", + }, + ] + + expect(config).toEqual({ + indentWith: '\t', + entries: [ + { + path: '/', + entries: testEntries, + }, + { + path: '/es/', + entries: testEntries, + }, + { + path: '/es/index.html', + entries: testEntries, + }, + { + path: '/fakeindex.html', + entries: testEntries, + }, + { + path: '/index.html', + entries: testEntries, + }, + ], + } satisfies NetlifyHeadersRawConfig) + }) }) describe('mergeNetlifyHeadersConfig', () => {