Files
parenthese/landing/test/csp-empreintes.test.mjs
T
IliesAllaliandClaude Opus 5.5 fcfb7d1494 Landing : pages préchargées au survol d'un lien (speculationrules)
- Règles de préchargement dans l'accueil et les 15 guides : une page du site est préparée quand on survole
  son lien (appui sur téléphone), et s'ouvre presque tout de suite. Pages seulement, pas les fichiers ;
  l'application n'est pas concernée. Ignoré par les navigateurs qui ne connaissent pas ces règles.
- Une page préchargée ne compte pas de visite avant d'être vraiment ouverte (PostHog attend
  prerenderingchange), et l'animation du logo se joue à l'ouverture, pas pendant le préchargement.
- CSP de la landing (deploy/nginx.conf) : empreinte des règles, et nouvelle empreinte du script du logo.
  Le mot-clé 'inline-speculation-rules' ne suffisait pas à Chrome.
- Test : chaque script en ligne des pages a son empreinte dans la CSP, chaque page a ses règles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:05:12 +01:00

40 lines
2.0 KiB
JavaScript

// Les scripts en ligne de la landing (animation du logo, règles de préchargement des pages) ne passent la CSP de
// nginx que par leur empreinte exacte (deploy/nginx.conf). Un script modifié sans recalculer son empreinte ne
// casse rien de visible : l'animation ou le préchargement s'arrêtent sans bruit. Ce test le dit tout de suite.
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { readFileSync, readdirSync, statSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const landing = fileURLToPath(new URL('..', import.meta.url))
const csp = readFileSync(path.join(landing, '../deploy/nginx.conf'), 'utf8').match(/map \$host \$parenthese_landing_csp \{\s*default "([^"]+)"/)[1]
const autorisees = new Set([...csp.matchAll(/'sha256-([^']+)'/g)].map((m) => m[1]))
const empreinte = (texte) => createHash('sha256').update(texte, 'utf8').digest('base64')
function pages(dossier) {
return readdirSync(dossier).flatMap((nom) => {
const chemin = path.join(dossier, nom)
if (statSync(chemin).isDirectory()) return pages(chemin)
return nom === 'index.html' ? [chemin] : []
})
}
const toutes = [path.join(landing, 'index.html'), ...pages(path.join(landing, 'public'))]
test('chaque script en ligne des pages a son empreinte dans la CSP de la landing', () => {
for (const fichier of toutes) {
const html = readFileSync(fichier, 'utf8')
for (const [, attributs, contenu] of html.matchAll(/<script([^>]*)>([\s\S]*?)<\/script>/g)) {
if (/\bsrc=|application\/ld\+json/.test(attributs)) continue
assert.ok(autorisees.has(empreinte(contenu)), `${path.relative(landing, fichier)} : script en ligne sans empreinte dans deploy/nginx.conf (${empreinte(contenu)})`)
}
}
})
test('chaque page a ses règles de préchargement', () => {
for (const fichier of toutes) {
assert.match(readFileSync(fichier, 'utf8'), /<script type="speculationrules">/, path.relative(landing, fichier))
}
})