/* ===================================================================== Prueft die Inhaltsrichtlinie (Content-Security-Policy). Bei dieser Kopfzeile ist der gefaehrliche Fehler NICHT, dass sie zu schwach ist -- sondern dass sie zu streng ist und die Seite lahmlegt. Und zwar erst im Browser des Besuchers: Der Deploy meldet Erfolg, der Server startet, die Seite laedt, und dann tut kein einziger Knopf mehr etwas. Es sieht aus wie kaputtes JavaScript und hat mit JavaScript nichts zu tun. Deshalb wird hier JEDE Seite in einem echten Browser geoeffnet und mitgeschrieben, was der Browser blockiert. Eine einzige blockierte Meldung laesst den Test durchfallen. Der zweite Teil ist die Gegenprobe: Eine Richtlinie, die alles erlaubt, wuerde den ersten Teil muehelos bestehen. Deshalb wird ausdruecklich geprueft, dass sie das Gefaehrliche auch WIRKLICH verbietet -- mit eingeschleustem Code, der scheitern muss. ===================================================================== */ import { chromium } from "playwright"; import express from "./server/node_modules/express/index.js"; import { inhaltsrichtlinie } from "./server/inhaltsrichtlinie.js"; import fs from "fs"; import path from "path"; const WURZEL = process.cwd(); let ok = 0, fehl = 0; const pruefe = (name, gut, info) => { if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); } else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); } }; const app = express(); app.use(inhaltsrichtlinie(WURZEL)); app.use(express.static(WURZEL, { index: "index.html" })); const server = app.listen(4408); await new Promise((f) => server.on("listening", f)); const browser = await chromium.launch(); /* Alle Seiten, die es gibt -- nicht nur eine Auswahl. Gerade die selten besuchten faellt sonst niemandem auf. */ const seitenListe = [ ...fs.readdirSync(WURZEL).filter((f) => f.endsWith(".html")).map((f) => "/" + f), ...fs.readdirSync(path.join(WURZEL, "webdesign")) .filter((f) => f.endsWith(".html")).map((f) => "/webdesign/" + f), ]; console.log(`1. BLOCKIERT DIE RICHTLINIE ETWAS? (${seitenListe.length} Seiten)`); const betroffen = []; for (const pfad of seitenListe) { const seite = await browser.newPage({ viewport: { width: 1280, height: 900 } }); const verstoesse = []; seite.on("console", (m) => { const t = m.text(); if (/Content Security Policy|Refused to (execute|load|apply)/i.test(t)) { verstoesse.push(t.slice(0, 150)); } }); await seite.goto("http://127.0.0.1:4408" + pfad, { waitUntil: "domcontentloaded" }) .catch(() => {}); await seite.waitForTimeout(700); if (verstoesse.length) betroffen.push({ pfad, verstoesse }); await seite.close(); } pruefe("keine Seite meldet einen Verstoss", betroffen.length === 0, betroffen.length ? betroffen.slice(0, 3).map((b) => b.pfad + ": " + b.verstoesse[0]).join(" | ") : seitenListe.length + " Seiten sauber"); if (betroffen.length) { console.log("\n Betroffene Seiten im Einzelnen:"); for (const b of betroffen) { console.log(" " + b.pfad); for (const v of b.verstoesse.slice(0, 2)) console.log(" " + v); } } console.log("\n2. STEHT UEBERALL EINE RICHTLINIE?"); /* Eine Pruefsumme kann nur dort stehen, wo es auch einen Inline-Block gibt. Zuerst hat dieser Test sie ueberall verlangt und bei portfolio.html und 404.html angeschlagen -- die haben schlicht keinen. Ein Test, der Unmoegliches fordert, wird frueher oder spaeter abgeschaltet; deshalb wird jetzt nach dem tatsaechlichen Inhalt der Datei unterschieden. */ for (const pfad of ["/index.html", "/webdesign/portfolio.html", "/404.html", "/webdesign/verwaltung.html"]) { const a = await fetch("http://127.0.0.1:4408" + pfad); const r = a.headers.get("content-security-policy") || ""; const quelle = fs.readFileSync(path.join(WURZEL, pfad), "utf8"); const hatInline = /]*\ssrc=)[^>]*>[\s\S]*?<\/script>/i.test(quelle); pruefe(` ${pfad}: Richtlinie gesetzt`, r.includes("script-src 'self'") && r.includes("object-src 'none'"), r ? r.slice(0, 46) + "…" : "FEHLT"); pruefe(` ${pfad}: ${hatInline ? "Pruefsumme vorhanden" : "keine noetig (kein Inline-Block)"}`, hatInline ? r.includes("'sha256-") : !r.includes("'sha256-")); } /* Der Kniff, auf den es bei diesem Aufbau ankommt: Aendert sich die HTML-Datei, muss die Pruefsumme SOFORT nachziehen. Sonst blockiert die Richtlinie nach dem naechsten "git pull" das Skript der eigenen Seite -- und zwar erst im Browser des Besuchers, nicht beim Deploy. */ console.log("\n2b. ZIEHT DIE PRUEFSUMME BEI EINER AENDERUNG NACH?"); const pruefDatei = path.join(WURZEL, "index.html"); const vorher = (await fetch("http://127.0.0.1:4408/index.html")) .headers.get("content-security-policy"); const inhaltVorher = fs.readFileSync(pruefDatei, "utf8"); try { /* Eine Zeile im Inline-Block aendern -- so, wie es eine echte Textaenderung taete. */ fs.writeFileSync(pruefDatei, inhaltVorher.replace("