Vollstaendiger Durchlauf ueber alle Pruefungen. Dabei kamen fuenf Dinge ans Licht, die teils seit Wochen offen standen. 1. WCAG-KONTRAST -- der einzige Punkt, der echte Besucher betraf Die lila Schilder erreichten nur 4,08:1, verlangt sind 4,5:1 fuer normalen Text. 17 Fundstellen, alle dieselbe Ursache: Die Schrift nutzte den vollen Ton Aurora Violet. Gemessen wurde nicht geschaetzt: Untergrund rgb(38,41,81) aus dem echten Bild ausgelesen, Schrift 12,5 Punkte -- und fett zaehlt erst ab 18,5 Punkten als "grosser Text". Neu ist --wd-lila-hell (#C197FF, 6,02:1). Bewusst NICHT der knappste Wert: #B380FF haette mit 4,91:1 gereicht, aber dasselbe Schild steht auch ueber der Buehne im Verwaltungsbereich. Ein Ton, der nur an einer Stelle knapp besteht, faellt beim naechsten Hintergrund wieder durch. Dasselbe Muster wie --wd-blau-hell, das genau deshalb existiert. 2. GEHEIMNIS-TEST -- der Test war veraltet, nicht der Code Er verlangte ".env hat Vorrang", die Umsetzung macht das Gegenteil. Die Begruendung im Code ueberzeugt: Wer PayPal-Daten im Formular eintraegt, erwartet, dass sie gelten. Andernfalls koennte ein alter Wert in der .env sie stumm ueberstimmen -- und man sucht stundenlang. Der Test prueft jetzt die tatsaechliche Reihenfolge, dazu neu, dass ein gleichzeitig vorhandener Serverwert auch angezeigt wird. Ausserdem endete der Lauf trotz gruener Pruefungen mit einer Fehlermeldung: Unter Windows haelt eine offene SQLite-Datei eine Sperre, das Aufraeumen scheiterte mit EPERM. Auf Linux waere es durchgelaufen -- dasselbe Skript mit unterschiedlichem Ergebnis je Rechner. Jetzt wird erst geschlossen, dann geloescht, und das Aufraeumen kann den Lauf nicht mehr zum Scheitern bringen. 3. PORTAL-TEST -- ebenfalls veraltet Er suchte "1500,00" und schlug fehl, seit der Formatierer den Tausenderpunkt setzt. "1.500,00 EUR" ist die korrekte deutsche Schreibweise; gerade ab vier Stellen macht der Punkt eine Zahl auf einen Blick lesbar. 4. ZWEI SEITEN OHNE WOERTERBUCH -- eine Entscheidung, keine Luecke "diagnose" ist ein Betriebswerkzeug. "rechtliches" ist der heiklere Fall: Rechtstexte durch eine ungepruefte Uebersetzung zu schicken ist gefaehrlicher, als sie einsprachig zu lassen. Ein Fehler in einer Widerrufsbelehrung wirkt gegen den Verfasser. Die Pruefung meldete beides als "Datei fehlt" -- das las sich wie ein Versehen und stand deshalb dauerhaft in der Fehlerliste, ohne dass jemand etwas tat. Jetzt sind beide benannt und begruendet. 5. TAG-UNGLEICHGEWICHT -- eine Fehlmessung Die Pruefung zaehlte auch HTML-Schnipsel, die als Zeichenketten im JavaScript stehen. Dort steht ein oeffnendes <div> regelmaessig in einer anderen Zeichenkette als sein </div>, weil die Teile erst beim Zusammensetzen ein Ganzes ergeben. Gegengeprueft im Browser: geparster Baum einwandfrei, kein Skriptfehler, Verschachtelung unauffaellig. Es war nie ein Strukturfehler. Die Meldung stand aber monatelang als "2 Fehler" da und haette jede echte Meldung entwertet, die dazugekommen waere. STAND Browser 717 Pruefungen 0 offen Server 566 Pruefungen 0 offen i18n keine Fehler WCAG 0 Fundstellen (vorher 17) Co-Authored-By: Claude Opus 5 <[email protected]>
134 lines
6.7 KiB
JavaScript
134 lines
6.7 KiB
JavaScript
/* Prueft die Ablage der PayPal-Zugangsdaten.
|
|
|
|
Der wichtigste Punkt ist NICHT, dass Speichern funktioniert, sondern
|
|
dass ein gespeichertes Geheimnis auf keinem Weg wieder herauskommt.
|
|
*/
|
|
import { mkdtempSync, rmSync, appendFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { randomBytes } from "node:crypto";
|
|
|
|
const tmp = mkdtempSync(join(tmpdir(), "wd-geh-"));
|
|
process.env.DB_PATH = join(tmp, "test.db");
|
|
process.env.ENCRYPTION_KEY = randomBytes(32).toString("base64");
|
|
delete process.env.PAYPAL_CLIENT_ID;
|
|
delete process.env.PAYPAL_CLIENT_SECRET;
|
|
delete process.env.PAYPAL_ENV;
|
|
|
|
const LOG = "/tmp/wd-geh.log";
|
|
const L = (...a) => { try { appendFileSync(LOG, a.join(" ") + "\n"); } catch (e) {} console.log(...a); };
|
|
|
|
const { db, initDb } = await import("./db.js");
|
|
initDb();
|
|
const geheim = await import("./lib/webdesign-geheimnisse.js");
|
|
const paypal = await import("./lib/webdesign-paypal.js");
|
|
|
|
let ok = 0, fehler = 0;
|
|
const pruefe = (n, b, z) => { if (b) { ok++; L(" ok " + n); } else { fehler++; L(" FEHL " + n + (z ? " -> " + z : "")); } };
|
|
|
|
const SECRET = 'EMeineHeikleZeichen&/\$"~UndLang1234567890';
|
|
|
|
L("\n1. SPEICHERN UND LESEN");
|
|
await geheim.geheimnisseLaden();
|
|
pruefe("vor dem Eintragen gilt PayPal als nicht eingerichtet", paypal.istEingerichtet("einmal") === false);
|
|
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_ID", "AbC-123");
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_SECRET", SECRET);
|
|
pruefe("danach gilt es als eingerichtet", paypal.istEingerichtet("einmal") === true);
|
|
pruefe("der Wert kommt zeichengenau zurueck", geheim.einstellung("PAYPAL_CLIENT_SECRET") === SECRET);
|
|
pruefe("Sonderzeichen ueberstehen die Verschluesselung",
|
|
geheim.einstellung("PAYPAL_CLIENT_SECRET").includes('&/\$"~'));
|
|
|
|
L("\n2. IN DER DATENBANK STEHT NICHTS LESBARES");
|
|
const roh = db.prepare("SELECT value FROM app_settings WHERE key = ?").get("wd_geheim_PAYPAL_CLIENT_SECRET");
|
|
pruefe("der Eintrag existiert", !!roh);
|
|
pruefe("er enthaelt NICHT den Klartext", roh && !roh.value.includes(SECRET), roh ? roh.value.slice(0, 30) : "-");
|
|
pruefe("er enthaelt auch keinen Teil davon", roh && !roh.value.includes("MeineHeikle"));
|
|
|
|
L("\n3. DER STAND VERRAET DEN WERT NICHT");
|
|
const st = geheim.stand();
|
|
const alsText = JSON.stringify(st);
|
|
pruefe("kein Geheimnis in der Antwort", !alsText.includes(SECRET) && !alsText.includes("MeineHeikle"));
|
|
pruefe("aber es ist erkennbar, DASS etwas da ist", st.PAYPAL_CLIENT_SECRET.gesetzt === true);
|
|
pruefe("die Laenge wird gemeldet", st.PAYPAL_CLIENT_SECRET.laenge === SECRET.length,
|
|
String(st.PAYPAL_CLIENT_SECRET.laenge));
|
|
pruefe("das Secret ist als geheim markiert", st.PAYPAL_CLIENT_SECRET.wert === null);
|
|
pruefe("die Betriebsart wird dagegen ausgeschrieben", st.PAYPAL_ENV.wert !== undefined);
|
|
pruefe("die Herkunft wird benannt", st.PAYPAL_CLIENT_SECRET.quelle === "verwaltung");
|
|
|
|
/* Frueher stand hier "DIE .env HAT VORRANG" -- und der Test schlug fehl,
|
|
weil die Umsetzung laengst die umgekehrte Reihenfolge hat.
|
|
|
|
Der Test war veraltet, nicht der Code. Die Begruendung steht im Kopf
|
|
von lib/webdesign-geheimnisse.js: Wer einen Wert im Formular setzt,
|
|
erwartet, dass er gilt. Waere es andersherum, koennte Filipe PayPal-
|
|
Zugangsdaten in der Verwaltung eintragen, ohne dass sich irgendetwas
|
|
aendert -- ein alter Wert in der .env wuerde sie stumm ueberstimmen.
|
|
|
|
Wichtig ist deshalb nicht nur die Reihenfolge selbst, sondern auch,
|
|
dass die Anzeige sie ehrlich benennt. Genau das wird hier mitgeprueft. */
|
|
L("\n4. DIE VERWALTUNG HAT VORRANG, DIE .env IST DER RUECKFALL");
|
|
process.env.PAYPAL_CLIENT_ID = "AUS-DER-ENV";
|
|
pruefe("ein Wert aus der Verwaltung sticht den Server",
|
|
geheim.einstellung("PAYPAL_CLIENT_ID") === "AbC-123");
|
|
pruefe("und die Anzeige nennt die Verwaltung als Quelle",
|
|
geheim.stand().PAYPAL_CLIENT_ID.quelle === "verwaltung");
|
|
/* Liegt in BEIDEN etwas, muss man das sehen koennen -- sonst sucht man
|
|
spaeter stundenlang, warum der Serverwert nicht wirkt. */
|
|
pruefe("dass auch auf dem Server einer liegt, wird vermerkt",
|
|
geheim.stand().PAYPAL_CLIENT_ID.auchAufServer === true);
|
|
|
|
/* Ohne Eintrag in der Verwaltung greift der Server-Wert. */
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_ID", "");
|
|
pruefe("ohne Verwaltungswert greift der Server",
|
|
geheim.einstellung("PAYPAL_CLIENT_ID") === "AUS-DER-ENV");
|
|
pruefe("und die Anzeige nennt dann den Server",
|
|
geheim.stand().PAYPAL_CLIENT_ID.quelle === "server");
|
|
|
|
delete process.env.PAYPAL_CLIENT_ID;
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_ID", "AbC-123");
|
|
pruefe("faellt der Server-Wert weg, greift wieder die Verwaltung",
|
|
geheim.einstellung("PAYPAL_CLIENT_ID") === "AbC-123");
|
|
|
|
L("\n5. NUR ERLAUBTE SCHLUESSEL");
|
|
let abgelehnt = false;
|
|
try { await geheim.einstellungSetzen("ADMIN_PASSWORT", "x"); } catch (e) { abgelehnt = true; }
|
|
pruefe("ein fremder Schluessel wird abgewiesen", abgelehnt);
|
|
let falscheArt = false;
|
|
try { await geheim.einstellungSetzen("PAYPAL_ENV", "vielleicht"); } catch (e) { falscheArt = true; }
|
|
pruefe("die Betriebsart erlaubt nur live oder sandbox", falscheArt);
|
|
|
|
L("\n6. LEEREN");
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_SECRET", "");
|
|
pruefe("ein leerer Wert entfernt den Eintrag", geheim.einstellung("PAYPAL_CLIENT_SECRET") === "");
|
|
pruefe("und die Zeile ist wirklich weg",
|
|
!db.prepare("SELECT 1 FROM app_settings WHERE key = ?").get("wd_geheim_PAYPAL_CLIENT_SECRET"));
|
|
pruefe("danach gilt PayPal wieder als nicht eingerichtet", paypal.istEingerichtet("einmal") === false);
|
|
|
|
L("\n7. FALSCHER SCHLUESSEL BRINGT DEN DIENST NICHT ZUM STEHEN");
|
|
await geheim.einstellungSetzen("PAYPAL_CLIENT_SECRET", SECRET);
|
|
process.env.ENCRYPTION_KEY = randomBytes(32).toString("base64");
|
|
let gestuerzt = false;
|
|
try { await geheim.geheimnisseLaden(); } catch (e) { gestuerzt = true; }
|
|
pruefe("das Laden mit falschem Schluessel wirft nicht", !gestuerzt);
|
|
pruefe("der Wert gilt dann als nicht vorhanden", geheim.einstellung("PAYPAL_CLIENT_SECRET") === "");
|
|
|
|
L(`\n===== ${ok} bestanden, ${fehler} fehlgeschlagen =====`);
|
|
/* Erst die Datenbank schliessen, DANN das Verzeichnis loeschen.
|
|
|
|
Unter Windows haelt eine offene SQLite-Datei eine Sperre auf ihre
|
|
Datei. Der Loeschversuch scheiterte dadurch mit EPERM, und der
|
|
Testlauf endete mit einer Fehlermeldung -- obwohl jede einzelne
|
|
Pruefung gruen war. Auf Linux faellt das nicht auf, weil sich dort
|
|
offene Dateien loeschen lassen: Dasselbe Skript haette je nach
|
|
Rechner ein anderes Ergebnis geliefert.
|
|
|
|
Das Aufraeumen selbst darf den Lauf nie zum Scheitern bringen. Ein
|
|
liegengebliebenes Temp-Verzeichnis ist ein Schoenheitsfehler, kein
|
|
Testergebnis. */
|
|
process.on("exit", () => {
|
|
try { db.close(); } catch (e) { /* war schon zu */ }
|
|
try { rmSync(tmp, { recursive: true, force: true }); } catch (e) { /* egal */ }
|
|
});
|
|
process.exitCode = fehler ? 1 : 0;
|