93c577c5d3
Snapshot de l'existant avant application du playbook OKI : background shader Three.js, carte graphe 3D, timeline statique.
87 lines
3.3 KiB
JavaScript
87 lines
3.3 KiB
JavaScript
/* Génère les fichiers de configuration CSP du déploiement statique :
|
|
- build/nginx-csp.conf → snippet à inclure dans la config nginx de l'app
|
|
YunoHost (cible de production : https://syel.o-k-i.net).
|
|
- build/.htaccess → équivalent Apache (secours, ignoré par nginx).
|
|
|
|
Pourquoi : SvelteKit démarre l'hydratation via un script inline dans le
|
|
HTML. Une CSP stricte sans « unsafe-inline » (ex. script-src 'self')
|
|
bloque ce bootstrap → site visible mais non interactif. Les empreintes
|
|
SHA-256 des scripts inline l'autorisent précisément, sans unsafe-inline.
|
|
Régénéré à chaque build (les empreintes changent avec les chunks). */
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const buildDir = fileURLToPath(new URL('../build/', import.meta.url));
|
|
|
|
function* walkHtml(dir) {
|
|
for (const entry of readdirSync(dir)) {
|
|
const full = join(dir, entry);
|
|
if (statSync(full).isDirectory()) yield* walkHtml(full);
|
|
else if (entry.endsWith('.html')) yield full;
|
|
}
|
|
}
|
|
|
|
const hashes = new Set();
|
|
|
|
for (const file of walkHtml(buildDir)) {
|
|
const html = readFileSync(file, 'utf8');
|
|
// Scripts inline uniquement (pas d'attribut src) — le navigateur empreinte
|
|
// le contenu texte exact du bloc.
|
|
for (const match of html.matchAll(/<script(?![^>]*\bsrc\s*=)[^>]*>([\s\S]*?)<\/script>/g)) {
|
|
const content = match[1];
|
|
if (!content.trim()) continue;
|
|
const digest = createHash('sha256').update(content, 'utf8').digest('base64');
|
|
hashes.add(`'sha256-${digest}'`);
|
|
}
|
|
}
|
|
|
|
if (hashes.size === 0) {
|
|
console.warn('postbuild-csp: aucun script inline trouvé — vérifier le build');
|
|
}
|
|
|
|
// Politique sobre pour un site statique autohébergé, + empreintes du bootstrap.
|
|
const csp = [
|
|
"default-src 'self'",
|
|
`script-src 'self' ${[...hashes].sort().join(' ')}`,
|
|
"style-src 'self' 'unsafe-inline'",
|
|
"img-src 'self'",
|
|
"font-src 'self'",
|
|
"connect-src 'self'",
|
|
"manifest-src 'self'",
|
|
"worker-src 'self'",
|
|
"frame-ancestors 'none'",
|
|
"base-uri 'self'",
|
|
"form-action 'self'",
|
|
"object-src 'none'"
|
|
].join('; ');
|
|
|
|
const nginx = `# Généré par scripts/postbuild-csp.mjs — ne pas éditer à la main.
|
|
# À inclure dans la config nginx de l'app YunoHost, dans le bloc location
|
|
# servant les fichiers statiques, ex. :
|
|
# location / {
|
|
# alias /var/www/<app>/;
|
|
# include /var/www/<app>/nginx-csp.conf;
|
|
# try_files $uri $uri/ =404;
|
|
# }
|
|
# Note nginx : un add_header au niveau le plus précis REMPLACE les add_header
|
|
# hérités des niveaux supérieurs — une éventuelle CSP globale est neutralisée.
|
|
# Régénéré à chaque build : le ré-uploader avec le reste du contenu de build/.
|
|
add_header Content-Security-Policy "${csp}" always;
|
|
`;
|
|
|
|
const htaccess = `# Généré par scripts/postbuild-csp.mjs — ne pas éditer à la main.
|
|
# Équivalent Apache du snippet nginx (secours uniquement — nginx l'ignore).
|
|
<IfModule mod_headers.c>
|
|
Header unset Content-Security-Policy
|
|
Header always unset Content-Security-Policy
|
|
Header set Content-Security-Policy "${csp}"
|
|
</IfModule>
|
|
`;
|
|
|
|
writeFileSync(join(buildDir, 'nginx-csp.conf'), nginx);
|
|
writeFileSync(join(buildDir, '.htaccess'), htaccess);
|
|
console.log(`postbuild-csp: nginx-csp.conf + .htaccess écrits (${hashes.size} empreinte(s))`);
|