Files
dogfather-universe/pruef-admin-schutz.mjs
T
DogFatherGitandClaude Opus 5 91bf0755cb Test: alle Admin-Routen automatisch auf Zugriffsschutz prüfen
Liest alle 109 /admin/-Routen direkt aus index.js (nicht hartkodiert)
und spricht jede mit einem ungültigen Token an. Erwartet 401/403.

Der Sinn: Der Schutz sitzt in jedem einzelnen Handler statt als Sperre
vor der Gruppe. Wer eine neue Route hinzufügt und den Aufruf vergisst,
macht sie unbemerkt öffentlich -- das fällt beim Klicken nie auf. Weil
der Test die Routen aus dem Code liest, taucht eine künftig vergessene
Absicherung automatisch als Fehlschlag auf, ohne dass jemand den Test
pflegt.

Ungültiges Token statt gar keins ist der gefahrlose Weg, das auch für
schreibende Routen (anlegen, löschen) gegen die echte Domain zu tun:
Der Schutz greift vor dem Handler, es kann nichts verändert werden.

Ergebnis live: 109 geschützt, 0 auffällig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:37:25 +02:00

96 lines
3.9 KiB
JavaScript

/* =====================================================================
Sind ALLE Admin-Routen wirklich geschützt?
WARUM AUTOMATISCH AUS DEM CODE GELESEN
Der Schutz der Verwaltungs-Endpunkte sitzt heute in jedem einzelnen
Handler, nicht als eine Sperre vor der ganzen Gruppe. Das funktioniert
-- solange niemand beim Hinzufügen einer neuen Route den Aufruf
vergisst. Genau dieser Fehler fällt beim Klicken nicht auf: Die neue
Route tut, was sie soll, sie ist nur zusätzlich für alle offen.
Ein Test mit einer von Hand gepflegten Routenliste hülfe hier nicht --
man würde dieselbe Zeile vergessen, die man auch beim Absichern
vergessen hat. Deshalb liest dieser Test die Routen direkt aus
index.js. Eine künftig hinzugefügte, ungeschützte Admin-Route taucht
damit automatisch als Fehlschlag auf, ohne dass jemand den Test pflegt.
WARUM MIT EINEM UNGÜLTIGEN TOKEN
Getestet wird gegen die echte, laufende Domain. Ein ungültiges
Bearer-Token ist der gefahrlose Weg, das auch für schreibende Routen
(anlegen, ablehnen, löschen) zu tun: Eine korrekt geschützte Route
lehnt das Token ab, BEVOR ihr Handler auch nur beginnt. Es kann also
nichts angelegt, geändert oder gelöscht werden. Kommt statt der
Ablehnung eine inhaltliche Antwort, ist genau das der Fund.
Aufruf: node pruef-admin-schutz.mjs
===================================================================== */
import { readFileSync } from "node:fs";
const BASIS = "https://postfach.dogfather-universe.com";
const INDEX = "server-internal/index.js";
/* Alle Routen aus index.js ziehen: app.get("...") / app.post("...") usw.
Danach auf Admin-Pfade eingrenzen. */
const quelle = readFileSync(INDEX, "utf8");
const muster = /app\.(get|post|put|patch|delete)\(\s*["'`]([^"'`]+)["'`]/g;
const routen = [];
for (const m of quelle.matchAll(muster)) {
const methode = m[1].toUpperCase();
const pfad = m[2];
if (/\/admin\//.test(pfad)) routen.push({ methode, pfad });
}
/* Platzhalter (:id, :kundeId ...) durch einen Wert ersetzen, der sicher
nirgends existiert -- die Route soll am fehlenden Token scheitern, nicht
an einer Objektsuche. */
const konkret = (pfad) => pfad.replace(/:[A-Za-z]+/g, "nichtexistent000");
let ok = 0, fehl = 0;
const funde = [];
const pruefe = (r, code) => {
/* 401 (nicht angemeldet) und 403 (angemeldet, aber ohne Recht) gelten
beide als "geschützt". Alles andere -- 200, 400, 404, 500 -- bedeutet:
die Anfrage kam am Türsteher vorbei. */
const geschuetzt = code === 401 || code === 403;
if (geschuetzt) { ok++; }
else {
fehl++;
funde.push(`${r.methode} ${r.pfad} -> ${code}`);
}
console.log(` ${geschuetzt ? "ok " : "FEHL"} ${String(code).padEnd(4)} ${r.methode.padEnd(6)} ${r.pfad}`);
};
console.log(`\n${routen.length} Admin-Routen aus index.js gelesen. Jede mit ungültigem Token:\n`);
for (const r of routen) {
const url = BASIS + konkret(r.pfad);
try {
const antwort = await fetch(url, {
method: r.methode,
headers: {
authorization: "Bearer ungueltig-testtoken-000",
"content-type": "application/json",
},
/* Leerer, gültiger JSON-Körper für schreibende Methoden, damit ein
etwaiger Body-Parser nicht schon vorher meckert -- wir wollen die
Token-Prüfung messen, nicht die Eingabevalidierung. */
body: r.methode === "GET" ? undefined : "{}",
signal: AbortSignal.timeout(15000),
});
pruefe(r, antwort.status);
} catch (e) {
fehl++;
funde.push(`${r.methode} ${r.pfad} -> keine Antwort (${String(e.message).slice(0, 30)})`);
console.log(` FEHL --- ${r.methode.padEnd(6)} ${r.pfad} (${String(e.message).slice(0, 30)})`);
}
}
console.log(`\n===== ${ok} geschützt, ${fehl} auffällig =====`);
if (funde.length) {
console.log("\nAuffällige Routen (kamen am Token-Schutz vorbei):");
for (const f of funde) console.log(" " + f);
}
process.exitCode = fehl ? 1 : 0;