From 3675ec1128675e007a1e341379bf28c8eebe6431 Mon Sep 17 00:00:00 2001
From: iPoower <101516206+iPoower@users.noreply.github.com>
Date: Tue, 27 Oct 2026 19:42:27 +0100
Subject: [PATCH] security(test): disallow document base overrides in CSP
---
index.html | 2 +-
public/_headers | 2 +-
scripts/security-check.ts | 4 +++-
tests/unit/security-check.test.ts | 6 ++++++
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/index.html b/index.html
index 03d8f2b..757889a 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,7 @@
Control Vault
diff --git a/public/_headers b/public/_headers
index 21a03d1..b7a1e1f 100644
--- a/public/_headers
+++ b/public/_headers
@@ -3,7 +3,7 @@
# Toute modification de ce fichier est une modification de sécurité : revue humaine obligatoire.
/*
- Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; manifest-src 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'
+ Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; manifest-src 'self'; worker-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
diff --git a/scripts/security-check.ts b/scripts/security-check.ts
index 13320c7..29007c3 100644
--- a/scripts/security-check.ts
+++ b/scripts/security-check.ts
@@ -53,7 +53,9 @@ export function checkCsp(csp: string, opts: { requireFrameAncestors: boolean }):
if (/'unsafe-inline'/.test(script)) problems.push("'unsafe-inline' interdit dans script-src");
if (/(^|\s)\*(\s|;|$)/.test(csp)) problems.push('joker * interdit');
if (!dir('object-src')?.includes("'none'")) problems.push("object-src 'none' requis");
- if (!dir('base-uri')) problems.push('base-uri requis');
+ const base = dir('base-uri');
+ if (!base) problems.push('base-uri requis');
+ else if (base.split(/\s+/).join(' ') !== "base-uri 'none'") problems.push("base-uri 'none' requis");
if (opts.requireFrameAncestors && !dir('frame-ancestors')?.includes("'none'")) problems.push("frame-ancestors 'none' requis");
return problems;
}
diff --git a/tests/unit/security-check.test.ts b/tests/unit/security-check.test.ts
index 38828c8..d43927f 100644
--- a/tests/unit/security-check.test.ts
+++ b/tests/unit/security-check.test.ts
@@ -36,6 +36,12 @@ describe('détection de secrets', () => {
});
describe('CSP', () => {
+ it.each(["'self'", 'https://fixture.invalid', "'none' 'self'"])(
+ 'refuse une base de document autorisée : %s', (base) => {
+ expect(checkCsp(`default-src 'self'; script-src 'self'; object-src 'none'; base-uri ${base}`,
+ { requireFrameAncestors: false })).toContain("base-uri 'none' requis");
+ });
+
it('refuse unsafe-eval, unsafe-inline dans script-src et le joker', () => {
expect(checkCsp("default-src 'self'; script-src 'self' 'unsafe-eval'; object-src 'none'; base-uri 'self'", { requireFrameAncestors: false })).toContain("'unsafe-eval' interdit");
expect(checkCsp("default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'", { requireFrameAncestors: false })).toContain("'unsafe-inline' interdit dans script-src");