Kasse und Google-Anmeldung in der Inhaltsrichtlinie freigegeben
Fortsetzung des Musik-Fundes: PayPal und Google haetten an derselben Stelle still versagt, sobald Dogi seine Zugangsdaten eintraegt -- leerer Bereich statt Bezahlknopf, ohne Fehlermeldung, ohne Protokolleintrag. Vorgehen bewusst gemessen statt geraten: Erst die Angaben der Anbieter (PayPal "Best Practices", Google CSP-Abschnitt der Setup-Anleitung), dann im echten Browser mit PayPals offizieller Testkennung "client-id=test" nachgemessen und die Verstoesse ueber das Ereignis securitypolicyviolation eingesammelt. Die Messung hat zwei Dinge gefunden, die in keiner Anleitung standen: - www.sandbox.paypal.com (frame-src + connect-src). Beim Einrichten testet man mit Sandbox-Zugangsdaten; ohne diesen Eintrag haette die Kasse in genau dieser Phase nicht abgeschlossen werden koennen. - accounts.google.com/gsi/style (style-src). 'unsafe-inline' deckt das NICHT ab -- es erlaubt nur Stile im Dokument, keine nachgeladene Stilvorlage. Der Anmelde-Knopf waere unformatiert erschienen. Bewusst einzelne Adressen statt PayPals vorgeschlagener Platzhalter (*.paypal.com): Was die Messung nicht gebraucht hat, steht nicht drin. Skripte bleiben ohne 'unsafe-inline' -- die Pruefsummen-Loesung fuer die eigenen Inline-Bloecke bleibt unangetastet, und ein zusaetzliches 'unsafe-inline' waere neben Pruefsummen ohnehin wirkungslos. Neuer Test pruef-kasse.mjs (15 Pruefungen): laedt das echte PayPal-SDK, baut die Bezahlknoepfe wirklich auf, rendert den echten Google-Knopf und verlangt NULL Verstoesse. Bestandstest pruef-inhaltsrichtlinie.mjs (22) und pruef-musik.mjs (17) bleiben gruen -- inklusive der Gegenprobe, dass eingeschleuste Skripte weiterhin blockiert werden. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
import { chromium } from "playwright";
|
||||
import { spawn } from "node:child_process";
|
||||
const server = spawn(process.execPath, ["index.js"], { cwd: "server", env: { ...process.env, PORT: "4434" }, stdio: "ignore" });
|
||||
const warte = async () => { for (let i=0;i<40;i++){ try { if ((await fetch("http://127.0.0.1:4434/index.html")).ok) return; } catch{} await new Promise(r=>setTimeout(r,250)); } throw new Error("kein Server"); };
|
||||
await warte();
|
||||
const b = await chromium.launch();
|
||||
const s = await b.newPage();
|
||||
await s.goto("http://127.0.0.1:4434/abonnieren.html", { waitUntil: "domcontentloaded" });
|
||||
await s.evaluate(() => { window.__v = []; document.addEventListener("securitypolicyviolation", (e) => window.__v.push({ dir: e.violatedDirective, blockiert: e.blockedURI })); });
|
||||
const paypal = await s.evaluate(async () => {
|
||||
const geladen = await new Promise((res) => { const sk = document.createElement("script"); sk.src = "https://www.paypal.com/sdk/js?client-id=test¤cy=EUR"; sk.onload = () => res(true); sk.onerror = () => res(false); document.body.appendChild(sk); setTimeout(() => res(false), 15000); });
|
||||
if (!geladen || !window.paypal) return { geladen, buttons: false };
|
||||
const ziel = document.createElement("div"); ziel.id = "pp-test"; ziel.style.width = "300px"; document.body.appendChild(ziel);
|
||||
try { await window.paypal.Buttons({}).render("#pp-test"); } catch (e) { return { geladen, buttons: false, fehler: String(e).slice(0,150) }; }
|
||||
const f = ziel.querySelector("iframe");
|
||||
return { geladen, buttons: !!f, rahmenHoehe: f ? Math.round(f.getBoundingClientRect().height) : 0 };
|
||||
});
|
||||
console.log("PAYPAL:", JSON.stringify(paypal));
|
||||
const google = await s.evaluate(async () => {
|
||||
const geladen = await new Promise((res) => { const sk = document.createElement("script"); sk.src = "https://accounts.google.com/gsi/client"; sk.onload = () => res(true); sk.onerror = () => res(false); document.head.appendChild(sk); setTimeout(() => res(false), 15000); });
|
||||
if (!geladen || !window.google) return { geladen, knopf: false };
|
||||
const ziel = document.createElement("div"); ziel.id = "g-test"; document.body.appendChild(ziel);
|
||||
try {
|
||||
window.google.accounts.id.initialize({ client_id: "000000000000-testtesttesttesttesttest.apps.googleusercontent.com", callback: () => {} });
|
||||
window.google.accounts.id.renderButton(ziel, { type: "standard" });
|
||||
} catch (e) { return { geladen, knopf: false, fehler: String(e).slice(0,150) }; }
|
||||
await new Promise(r => setTimeout(r, 4000));
|
||||
return { geladen, knopf: ziel.children.length > 0, inhalt: ziel.innerHTML.slice(0, 60) };
|
||||
});
|
||||
console.log("GOOGLE:", JSON.stringify(google));
|
||||
await s.waitForTimeout(3000);
|
||||
const v = await s.evaluate(() => window.__v);
|
||||
const zus = {};
|
||||
for (const e of v) { const k = e.dir + " <- " + (e.blockiert || "(inline)"); zus[k] = (zus[k]||0)+1; }
|
||||
console.log("\nVERSTOESSE:", Object.keys(zus).length === 0 ? "KEINE" : "");
|
||||
Object.entries(zus).sort().forEach(([k,n]) => console.log(" " + n + "x " + k));
|
||||
console.log("\nRahmen:"); s.frames().forEach(f => console.log(" " + f.url().slice(0,75)));
|
||||
await b.close(); server.kill();
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/* =====================================================================
|
||||
Kasse & Anmeldung gegen die Inhaltsrichtlinie (27.08.2026)
|
||||
|
||||
WORUM ES GEHT
|
||||
|
||||
Auf abonnieren.html kommen zwei Dinge von fremden Servern: die
|
||||
PayPal-Kasse und die Google-Anmeldung. Beide werden erst NACH einer
|
||||
Nutzerhandlung nachgeladen (Klick bzw. Anmeldung) und beide sind
|
||||
derzeit nicht scharf geschaltet -- googleClientId und paypalClientId
|
||||
stehen auf null. Ein Fehler dort fällt deshalb heute NICHT auf. Er
|
||||
fällt erst an dem Tag auf, an dem Dogi seine echten Zugangsdaten
|
||||
einträgt: Dann stünde an der Kasse ein leerer Bereich, ohne
|
||||
Fehlermeldung, ohne Eintrag im Server-Protokoll.
|
||||
|
||||
Genau dieses Muster hat schon einmal zugeschlagen (Musik-Knopf,
|
||||
26.-27.08.2026). Deshalb wird hier vorgezogen geprüft, statt zu warten.
|
||||
|
||||
WIE ECHT DIESER TEST IST
|
||||
|
||||
- Er startet den ECHTEN server/index.js, damit die echte Kopfzeile
|
||||
ausgeliefert wird. Ein Datei-Server erzeugt sie gar nicht.
|
||||
- Er lädt das ECHTE PayPal-SDK mit deren offizieller Testkennung
|
||||
("client-id=test") und baut die Knöpfe wirklich auf.
|
||||
- Er lädt die ECHTE Google-Bibliothek und rendert den Knopf wirklich.
|
||||
- Verstöße werden nicht aus Konsolentext geraten, sondern über das
|
||||
Ereignis "securitypolicyviolation" gezählt -- dort stehen Richtlinie
|
||||
und blockierte Adresse maschinenlesbar drin.
|
||||
|
||||
Kein echter Zugangscode und kein echtes Geld sind beteiligt.
|
||||
===================================================================== */
|
||||
import { chromium } from "playwright";
|
||||
import { spawn } from "node:child_process";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||||
const PORT = 4435;
|
||||
const BASIS = `http://127.0.0.1:${PORT}`;
|
||||
|
||||
let ok = 0, fehl = 0;
|
||||
const funde = [];
|
||||
const pruefe = (name, gut, info) => {
|
||||
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
|
||||
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); funde.push(name + " — " + info); }
|
||||
};
|
||||
|
||||
const server = spawn(process.execPath, ["index.js"], {
|
||||
cwd: join(HIER, "server"),
|
||||
env: { ...process.env, PORT: String(PORT) },
|
||||
stdio: "ignore",
|
||||
});
|
||||
const beenden = (code) => { try { server.kill(); } catch { /* schon weg */ } process.exit(code); };
|
||||
/* Notbremse: PayPals render() wartet ohne echten Bezahlvorgang unbegrenzt.
|
||||
Ohne diese Grenze bliebe der Test im Zweifel ewig hängen. */
|
||||
const notbremse = setTimeout(() => { console.log("\nZEIT ABGELAUFEN — Testlauf abgebrochen."); beenden(1); }, 180000);
|
||||
notbremse.unref();
|
||||
|
||||
async function warteAufServer() {
|
||||
for (let i = 0; i < 40; i++) {
|
||||
try {
|
||||
const res = await fetch(BASIS + "/abonnieren.html");
|
||||
if (res.ok) return res;
|
||||
} catch { /* noch nicht oben */ }
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
}
|
||||
throw new Error("Server ist nicht hochgekommen");
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const antwort = await warteAufServer();
|
||||
const richtlinie = antwort.headers.get("content-security-policy") || "";
|
||||
const regel = (name) => (richtlinie.split(";").find((t) => t.trim().startsWith(name + " ")) || "").trim();
|
||||
|
||||
console.log("\n== Was die Kopfzeile verspricht ==");
|
||||
pruefe("script-src kennt PayPal", /www\.paypal\.com/.test(regel("script-src")), regel("script-src").slice(0, 120));
|
||||
pruefe("script-src kennt Google", /accounts\.google\.com\/gsi\/client/.test(regel("script-src")), "");
|
||||
pruefe("frame-src kennt PayPal (Betrieb + Übung)", /www\.paypal\.com/.test(regel("frame-src")) && /sandbox\.paypal\.com/.test(regel("frame-src")), regel("frame-src"));
|
||||
pruefe("frame-src kennt Google", /accounts\.google\.com\/gsi\//.test(regel("frame-src")), "");
|
||||
pruefe("style-src kennt Googles Stilvorlage", /accounts\.google\.com\/gsi\/style/.test(regel("style-src")), regel("style-src"));
|
||||
pruefe("Musik bleibt erlaubt", /open\.spotify\.com/.test(regel("frame-src")), "");
|
||||
pruefe("Kein Sternchen in frame-src", !/\*/.test(regel("frame-src")), regel("frame-src"));
|
||||
pruefe("Kein 'unsafe-inline' bei Skripten", !/unsafe-inline/.test(regel("script-src")), "");
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const seite = await browser.newPage({ viewport: { width: 1280, height: 900 } });
|
||||
await seite.goto(BASIS + "/abonnieren.html", { waitUntil: "domcontentloaded" });
|
||||
await seite.evaluate(() => {
|
||||
window.__verstoesse = [];
|
||||
document.addEventListener("securitypolicyviolation", (e) =>
|
||||
window.__verstoesse.push({ regel: e.violatedDirective, blockiert: e.blockedURI || "(inline)" })
|
||||
);
|
||||
});
|
||||
|
||||
/* ---------- PayPal ---------- */
|
||||
console.log("\n== PayPal-Kasse (offizielle Testkennung) ==");
|
||||
const paypal = await seite.evaluate(async () => {
|
||||
const geladen = await new Promise((res) => {
|
||||
const sk = document.createElement("script");
|
||||
sk.src = "https://www.paypal.com/sdk/js?client-id=test¤cy=EUR";
|
||||
sk.onload = () => res(true);
|
||||
sk.onerror = () => res(false);
|
||||
document.body.appendChild(sk);
|
||||
setTimeout(() => res(false), 20000);
|
||||
});
|
||||
if (!geladen || !window.paypal) return { geladen, knoepfe: false };
|
||||
const ziel = document.createElement("div");
|
||||
ziel.id = "kassen-test";
|
||||
ziel.style.width = "320px";
|
||||
document.body.appendChild(ziel);
|
||||
/* Absichtlich NICHT abwarten: render() loest ohne echten Bezahlvorgang
|
||||
nie auf. Es geht nur darum, ob der Rahmen entstehen darf. */
|
||||
try { window.paypal.Buttons({}).render("#kassen-test").catch(() => {}); } catch { /* egal */ }
|
||||
await new Promise((r) => setTimeout(r, 9000));
|
||||
const rahmen = ziel.querySelector("iframe");
|
||||
return { geladen, knoepfe: !!rahmen, hoehe: rahmen ? Math.round(rahmen.getBoundingClientRect().height) : 0 };
|
||||
});
|
||||
pruefe("PayPal-SDK wird geladen", paypal.geladen, paypal.geladen ? "geladen" : "BLOCKIERT");
|
||||
pruefe("Bezahlknöpfe werden aufgebaut", paypal.knoepfe, paypal.knoepfe ? paypal.hoehe + "px hoch" : "kein Rahmen entstanden");
|
||||
|
||||
/* ---------- Google ---------- */
|
||||
console.log("\n== Google-Anmeldung ==");
|
||||
const google = await seite.evaluate(async () => {
|
||||
const geladen = await new Promise((res) => {
|
||||
const sk = document.createElement("script");
|
||||
sk.src = "https://accounts.google.com/gsi/client";
|
||||
sk.onload = () => res(true);
|
||||
sk.onerror = () => res(false);
|
||||
document.head.appendChild(sk);
|
||||
setTimeout(() => res(false), 20000);
|
||||
});
|
||||
if (!geladen || !window.google) return { geladen, knopf: false };
|
||||
const ziel = document.createElement("div");
|
||||
ziel.id = "anmelde-test";
|
||||
document.body.appendChild(ziel);
|
||||
try {
|
||||
window.google.accounts.id.initialize({
|
||||
client_id: "000000000000-testtesttesttesttesttest.apps.googleusercontent.com",
|
||||
callback: () => {},
|
||||
});
|
||||
window.google.accounts.id.renderButton(ziel, { type: "standard" });
|
||||
} catch { return { geladen, knopf: false }; }
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
return { geladen, knopf: ziel.children.length > 0 };
|
||||
});
|
||||
pruefe("Google-Bibliothek wird geladen", google.geladen, google.geladen ? "geladen" : "BLOCKIERT");
|
||||
pruefe("Anmelde-Knopf wird aufgebaut", google.knopf, google.knopf ? "gerendert" : "leer geblieben");
|
||||
const googleRahmen = seite.frames().filter((f) => f.url().startsWith("https://accounts.google.com/gsi/")).length;
|
||||
pruefe("Googles Rahmen darf laden", googleRahmen >= 1, googleRahmen + " Rahmen");
|
||||
|
||||
/* ---------- Das eigentliche Urteil ---------- */
|
||||
console.log("\n== Verstöße gegen die Inhaltsrichtlinie ==");
|
||||
const verstoesse = await seite.evaluate(() => window.__verstoesse);
|
||||
const gezaehlt = {};
|
||||
for (const v of verstoesse) {
|
||||
const schluessel = v.regel + " <- " + v.blockiert.split("?")[0];
|
||||
gezaehlt[schluessel] = (gezaehlt[schluessel] || 0) + 1;
|
||||
}
|
||||
Object.entries(gezaehlt).forEach(([k, n]) => console.log(" " + n + "x " + k));
|
||||
pruefe("Kein einziger Verstoß", verstoesse.length === 0, verstoesse.length + " Verstöße");
|
||||
|
||||
const fehlerRahmen = seite.frames().filter((f) => f.url().startsWith("chrome-error://")).length;
|
||||
pruefe("Kein Rahmen endet als Fehlerseite", fehlerRahmen === 0, fehlerRahmen + " Fehlerseiten");
|
||||
|
||||
/* Bilder von genau den beiden Testelementen.
|
||||
|
||||
WICHTIG: Die Elemente werden dafür NICHT im Seitenbaum verschoben.
|
||||
PayPal baut seine Knöpfe wieder ab, sobald ihr Rahmen umgehängt wird
|
||||
— ein Bild davon zeigte dann fälschlich eine leere Kasse, obwohl sie
|
||||
kurz zuvor sauber stand. */
|
||||
for (const [wahl, datei] of [["#kassen-test", "bild-kasse-paypal.png"], ["#anmelde-test", "bild-kasse-google.png"]]) {
|
||||
try {
|
||||
await seite.locator(wahl).scrollIntoViewIfNeeded();
|
||||
await seite.waitForTimeout(600);
|
||||
await seite.locator(wahl).screenshot({ path: datei });
|
||||
} catch { /* Bild ist Beiwerk, kein Prüfkriterium */ }
|
||||
}
|
||||
await browser.close();
|
||||
clearTimeout(notbremse);
|
||||
console.log("\n===== " + ok + " ok, " + fehl + " Fehler =====");
|
||||
if (funde.length) { console.log("\nOffene Punkte:"); funde.forEach((f) => console.log(" - " + f)); }
|
||||
beenden(fehl ? 1 : 0);
|
||||
})().catch((e) => {
|
||||
console.error("Testlauf abgebrochen:", e.message);
|
||||
beenden(1);
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
/* =====================================================================
|
||||
Wie schwer und wie schnell sind die öffentlichen Seiten?
|
||||
|
||||
Misst je Seite die übertragene Datenmenge und die Ladezeit bis
|
||||
"fertig geladen", und sammelt die größten Einzelressourcen über alle
|
||||
Seiten. Ziel ist NICHT ein grüner Haken, sondern das Finden konkreter
|
||||
Brocken, die sich lohnen zu verkleinern -- so wie am 27.08. das 2,5-MB-
|
||||
PNG "Event des Jahres" gefunden wurde.
|
||||
|
||||
Gegen die echte Domain, mit leerem Cache (jeder Aufruf frische Seite).
|
||||
===================================================================== */
|
||||
import { chromium } from "playwright";
|
||||
|
||||
const BASIS = "https://dogfather-universe.com";
|
||||
const SEITEN = [
|
||||
"/", "/dogfather.html", "/hasidog.html", "/manager.html", "/casper.html",
|
||||
"/vanvan.html", "/tt-muddi.html", "/zeitreise.html", "/streamer.html",
|
||||
"/community.html", "/stimmen.html", "/streamplan.html", "/team-modis.html",
|
||||
"/team-scouts.html", "/creator.html", "/werte.html", "/bewerben.html",
|
||||
"/kontakt.html", "/links.html", "/supporter.html", "/abonnieren.html",
|
||||
"/medien-dogfather.html", "/medien-hasidog.html", "/medien-casper.html",
|
||||
];
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const groessteJeRessource = new Map(); // url -> bytes
|
||||
const proSeite = [];
|
||||
|
||||
for (const pfad of SEITEN) {
|
||||
const seite = await browser.newPage();
|
||||
let bytes = 0;
|
||||
seite.on("response", async (r) => {
|
||||
try {
|
||||
const h = r.headers();
|
||||
const len = Number(h["content-length"] || 0);
|
||||
let b = len;
|
||||
if (!b) { try { b = (await r.body()).length; } catch { b = 0; } }
|
||||
bytes += b;
|
||||
const u = r.url();
|
||||
if (b > (groessteJeRessource.get(u) || 0)) groessteJeRessource.set(u, b);
|
||||
} catch { /* egal */ }
|
||||
});
|
||||
const start = Date.now();
|
||||
try {
|
||||
await seite.goto(BASIS + pfad, { waitUntil: "networkidle", timeout: 45000 });
|
||||
} catch { /* trotzdem messen, was da war */ }
|
||||
const dauer = Date.now() - start;
|
||||
proSeite.push({ pfad, kb: Math.round(bytes / 1024), ms: dauer });
|
||||
await seite.close();
|
||||
}
|
||||
await browser.close();
|
||||
|
||||
console.log("\n=== Pro Seite (übertragen / Ladezeit), schwerste zuerst ===");
|
||||
for (const s of proSeite.sort((a, b) => b.kb - a.kb)) {
|
||||
const warn = s.kb > 2000 ? " ⚠ schwer" : s.kb > 1000 ? " · mittel" : "";
|
||||
console.log(` ${String(s.kb).padStart(5)} KB ${String(s.ms).padStart(5)} ms ${s.pfad}${warn}`);
|
||||
}
|
||||
const schnitt = Math.round(proSeite.reduce((a, s) => a + s.kb, 0) / proSeite.length);
|
||||
console.log(` ---\n Schnitt: ${schnitt} KB / Seite`);
|
||||
|
||||
console.log("\n=== Die 12 größten Einzelressourcen (Kandidaten zum Verkleinern) ===");
|
||||
const top = [...groessteJeRessource].sort((a, b) => b[1] - a[1]).slice(0, 12);
|
||||
for (const [u, b] of top) {
|
||||
const kb = Math.round(b / 1024);
|
||||
const kurz = u.replace(BASIS, "").replace(/\?.*$/, "");
|
||||
const warn = kb > 500 ? " ⚠" : "";
|
||||
console.log(` ${String(kb).padStart(5)} KB ${kurz}${warn}`);
|
||||
}
|
||||
@@ -50,6 +50,61 @@ import crypto from "crypto";
|
||||
import { readFileSync, statSync } from "node:fs";
|
||||
import { join, normalize } from "node:path";
|
||||
|
||||
/* =====================================================================
|
||||
FREMDE QUELLEN — hier stehen sie namentlich, nicht verstreut
|
||||
|
||||
Drei Dinge auf dieser Seite kommen nicht vom eigenen Server: die
|
||||
Spotify-Player im Musik-Feld, die PayPal-Kasse und die
|
||||
Google-Anmeldung auf abonnieren.html.
|
||||
|
||||
Warum sie hier zusammenstehen: Wer eine Quelle vergisst, merkt es
|
||||
NICHT beim Deploy und nicht im Server-Protokoll. Der Browser wirft
|
||||
das Element still weg -- genau so ist am 26.08.2026 der Musik-Knopf
|
||||
ausgefallen und erst am 27.08. aufgefallen. Eine Liste an einer
|
||||
Stelle, mit Begründung je Eintrag, ist die einzige Stelle, an der man
|
||||
beim nächsten Mal nachsieht.
|
||||
|
||||
Ausgewählt wurde nach den Angaben der Anbieter (PayPal: "Best
|
||||
Practices" der JS-SDK-Doku, Google: CSP-Abschnitt der Setup-Anleitung)
|
||||
und danach mit pruef-kasse.mjs im echten Browser nachgemessen: Die
|
||||
Kasse wird dort mit PayPals offizieller Testkennung wirklich
|
||||
aufgebaut, die Anmeldung wirklich gerendert. Was dabei nicht gebraucht
|
||||
wurde, steht hier auch nicht -- deshalb einzelne Adressen statt der
|
||||
von PayPal vorgeschlagenen Platzhalter *.paypal.com.
|
||||
===================================================================== */
|
||||
|
||||
/* Spotify: die zwei Player im Musik-Feld (main.js, jede Seite). */
|
||||
const SPOTIFY = "https://open.spotify.com";
|
||||
|
||||
/* PayPal: das SDK selbst liegt auf www.paypal.com, Bilder/Schriften der
|
||||
Knöpfe auf www.paypalobjects.com. Der Bezahlvorgang läuft anschließend
|
||||
in einem Rahmen von www.paypal.com.
|
||||
|
||||
⚠️ Die Übungs-Umgebung (www.sandbox.paypal.com) steht bewusst MIT
|
||||
drin, obwohl im Betrieb nur www.paypal.com gebraucht wird. Grund:
|
||||
Beim Einrichten testet man zuerst mit Sandbox-Zugangsdaten -- das SDK
|
||||
spricht dann ausschließlich mit der Sandbox-Adresse. Ohne diesen
|
||||
Eintrag hätte Dogi beim ersten Testkauf eine Kasse gesehen, die sich
|
||||
nicht abschließen lässt, ohne jeden Hinweis auf den Grund. Genau
|
||||
dieser Verstoß ist bei der Messung mit PayPals Testkennung
|
||||
aufgetreten -- geraten hätte man ihn nicht. */
|
||||
const PAYPAL_SKRIPT = "https://www.paypal.com https://www.paypalobjects.com";
|
||||
const PAYPAL_RAHMEN = "https://www.paypal.com https://www.sandbox.paypal.com";
|
||||
const PAYPAL_BILDER = "https://www.paypal.com https://www.paypalobjects.com";
|
||||
const PAYPAL_VERBINDUNG = "https://www.paypal.com https://www.paypalobjects.com https://www.sandbox.paypal.com";
|
||||
|
||||
/* Google-Anmeldung: Google nennt ausdrücklich die ELTERN-Adresse
|
||||
.../gsi/ statt einzelner Endpunkte -- damit die Anmeldung nicht bricht,
|
||||
wenn Google intern eine Adresse ändert. */
|
||||
const GOOGLE_SKRIPT = "https://accounts.google.com/gsi/client";
|
||||
const GOOGLE_RAHMEN = "https://accounts.google.com/gsi/";
|
||||
const GOOGLE_VERBINDUNG = "https://accounts.google.com/gsi/";
|
||||
/* Der Anmelde-Knopf holt sein Aussehen als eigene Stilvorlage. Wichtig:
|
||||
'unsafe-inline' weiter unten deckt das NICHT ab -- das erlaubt nur
|
||||
Stile IM Dokument, keine nachgeladene Datei von fremder Adresse. Ohne
|
||||
diesen Eintrag erscheint der Knopf unfertig/unformatiert. */
|
||||
const GOOGLE_STIL = "https://accounts.google.com/gsi/style";
|
||||
|
||||
/* Gemerkte Prüfsummen je Datei, zusammen mit der Änderungszeit, aus der
|
||||
sie stammen. Ändert sich die Datei, wird neu gerechnet. */
|
||||
const merker = new Map();
|
||||
@@ -151,13 +206,13 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
wenn ohnehin etwas grundlegend nicht stimmt. */
|
||||
if (!summen) return next();
|
||||
|
||||
const skript = ["'self'", ...summen].join(" ");
|
||||
const skript = ["'self'", ...summen, PAYPAL_SKRIPT, GOOGLE_SKRIPT].join(" ");
|
||||
|
||||
res.setHeader("Content-Security-Policy", [
|
||||
"default-src 'self'",
|
||||
`script-src ${skript}`,
|
||||
/* Siehe Kopf der Datei: bewusste Ausnahme, eigener Punkt auf der Liste. */
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
`style-src 'self' 'unsafe-inline' ${GOOGLE_STIL}`,
|
||||
/* data: für eingebettete Kleingrafiken, blob: für selbst erzeugte
|
||||
Vorschauen beim Hochladen.
|
||||
|
||||
@@ -169,12 +224,12 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
ihn: Der Deploy hätte Erfolg gemeldet, der Server wäre
|
||||
gestartet, und der Schaden wäre erst jemandem aufgefallen, der
|
||||
die Seite ansieht. */
|
||||
"img-src 'self' data: blob: https://postfach.dogfather-universe.com",
|
||||
`img-src 'self' data: blob: https://postfach.dogfather-universe.com ${PAYPAL_BILDER}`,
|
||||
"font-src 'self'",
|
||||
/* Die Seite spricht mit dem eigenen Server und mit dem internen
|
||||
Bereich unter postfach. -- mehr nicht. Ein eingeschleustes
|
||||
Skript könnte damit keine Daten nach außen schicken. */
|
||||
"connect-src 'self' https://postfach.dogfather-universe.com",
|
||||
`connect-src 'self' https://postfach.dogfather-universe.com ${PAYPAL_VERBINDUNG} ${GOOGLE_VERBINDUNG}`,
|
||||
/* Kein Flash, keine Java-Applets, keine eingebetteten Objekte. */
|
||||
"object-src 'none'",
|
||||
/* Verhindert, dass ein eingeschleustes <base> alle relativen
|
||||
@@ -209,8 +264,10 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
Google-Anmeldung auf abonnieren.html arbeiten ebenfalls mit
|
||||
Rahmen), muss er hier NAMENTLICH ergänzt werden -- sonst
|
||||
wiederholt sich exakt dieser stille Ausfall an der Kasse.
|
||||
pruef-musik.mjs prüft das im echten Browser mit. */
|
||||
"frame-src https://open.spotify.com",
|
||||
pruef-musik.mjs und pruef-kasse.mjs prüfen das im echten Browser
|
||||
mit -- inklusive der Kasse, die vorher genau hier gescheitert
|
||||
wäre. */
|
||||
`frame-src ${SPOTIFY} ${PAYPAL_RAHMEN} ${GOOGLE_RAHMEN}`,
|
||||
"worker-src 'self'",
|
||||
/* Hebt gemischte Inhalte auf HTTPS, statt sie zu blockieren. */
|
||||
"upgrade-insecure-requests",
|
||||
|
||||
Reference in New Issue
Block a user