/* ===================================================================== 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;