Files
opencrm/backend/src/services/pflichtrechte.service.ts
T
duffyduckandClaude Opus 5 cca242119b R188/R189-01 aus dem Pentest umgesetzt - Klasse statt Instanz
Beide Findings der Pentesterin waren berechtigt. Ihre Patches liessen
sich nicht anwenden (Basis 791711c, seitdem 42 Commits, sync-roles.ts
kollidiert), und an zwei Stellen greifen sie zu kurz.

R188 - ungueltige IDs im Pfad
-----------------------------
Gemeldet: GET /api/users/:id gibt bei nicht-numerischer ID 500 statt 400.
Ihr Fix schliesst nebenbei mehr, als sie beansprucht: parseInt('12abc')
ergibt 12, also lieferte /api/users/12abc bisher Benutzer 12 aus.

Es waren aber 181 ungepruefte Stellen in 19 Controllern, nicht eine. 181
Einzel-Guards waeren genau der Fehler aus R186-01 gewesen - drei
Filterlisten, die dasselbe bedeuten sollten und auseinanderliefen.
Stattdessen router.param(), an einer Stelle fuer alle 33 Router
registriert, ueber einen mounte()-Helfer, der Pruefung und Einhaengen
zusammenbindet.

Antwort ist 404, nicht 400: Ein Pfadsegment, das keine ID sein kann,
benennt keine Ressource. Der bestehende Praezedenzfall in
provider.controller.ts (Pentest Mai 2026) hatte es genauso entschieden.

Dabei eine aeltere Heuristik abgeloest (Pentest Runde 7). Ihr eigener
Kommentar nannte den Grund fuer sie - "app.param() greift nicht auf in
Sub-Router gemounteten Routes" - und genau das loest mounte(). Sie war zu
eng (/users/abc ging durch und endete als 500) und zu weit (ein
Einstellungs-Schluessel 12abc unter :key wurde geblockt, obwohl das keine
ID ist), und sie antwortete 400, wo jetzt 404 steht.

R189-01 - DSGVO-Rechte ohne Traeger
------------------------------------
Gemeldet: gdpr:* und audit:read/export haengen an DSGVO und Developer,
die Admin-Rolle hat sie nicht, und nach einem frischen Seed war DSGVO
keinem Konto zugewiesen. Auskunft nach Art. 15 und Loeschung nach Art. 17
konnte niemand ausfuehren.

Seed weist admin@admin.com jetzt zusaetzlich die DSGVO-Rolle zu; die
Admin-Rolle selbst bleibt ohne diese Rechte, die Trennung aus R186 bleibt
also erhalten. Label ehrlich gemacht.

Beim Pruefen ihres Patches ein eigener Fund: seed.ts vergab an die
DSGVO-Rolle weiterhin audit:* komplett, inklusive audit:admin - die
Buendelung, die fc6f39e aufgeloest hat. Ich hatte damals zwei Listen
gefunden und die dritte uebersehen. Gerettet hat es nur die Reihenfolge
im Containerstart; ein einzelnes `npm run db:seed` brachte sie zurueck.

Der Seed hilft nur bei Neuinstallation (update: {}). Deshalb zusaetzlich
eine Wache beim Start: Gibt es fuer gdpr:export, gdpr:delete oder
audit:read kein aktives Konto, steht das mit Handlungsanweisung im Log -
Erkennung der ABWESENHEIT einer Faehigkeit, wie beim Heartbeat. Bewusst
nur melden, nicht automatisch vergeben.

Getestet ueber HTTP gegen eine Wegwerf-Instanz: alle ID-Varianten quer
ueber sechs Controller, Nicht-ID-Parameter unveraendert, frischer Seed,
Wache mit und ohne vergebene Rechte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:17:12 +02:00

83 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import prisma from '../lib/prisma.js';
/**
* Prueft beim Start, ob die gesetzlich gebundenen Rechte ueberhaupt jemand
* ausueben kann (Pentest R189-01).
*
* Hintergrund: `gdpr:export`, `gdpr:delete` und `gdpr:admin` haengen an den
* Rollen DSGVO und Developer nicht an der Admin-Rolle. Nach einem frischen
* Seed war keine der beiden einem Konto zugewiesen. Ergebnis: Eine Auskunft
* nach Art. 15 oder eine Loeschung nach Art. 17 konnte NIEMAND ausfuehren,
* obwohl mehrere „Admin"-Konten existierten. Ein Ausfall mit Fristwirkung,
* ausgeloest durch nichts weiter als eine Neuinstallation.
*
* Der Seed weist das Recht jetzt zu aber nur bei NEUINSTALLATION. Auf einer
* laufenden Datenbank aendert er nichts, und niemand merkt es, bis es darauf
* ankommt. Deshalb diese Wache: Sie erkennt die ABWESENHEIT einer Faehigkeit,
* so wie der Heartbeat das Ausbleiben eines Dienstkontos erkennt.
*
* Bewusst nur eine Meldung, keine automatische Vergabe: Rechte zu verteilen,
* ohne dass ein Mensch es veranlasst hat, waere der groessere Fehler.
*/
/** Rechte, deren Fehlen ein rechtliches und kein technisches Problem ist. */
const PFLICHTRECHTE: Array<{ resource: string; action: string; wofuer: string }> = [
{ resource: 'gdpr', action: 'export', wofuer: 'Auskunft nach Art. 15 DSGVO' },
{ resource: 'gdpr', action: 'delete', wofuer: 'Löschung nach Art. 17 DSGVO' },
{ resource: 'audit', action: 'read', wofuer: 'Prüfung des Audit-Protokolls' },
];
export async function pruefePflichtrechte(): Promise<void> {
try {
const fehlend: string[] = [];
for (const recht of PFLICHTRECHTE) {
const traeger = await prisma.user.count({
where: {
isActive: true,
roles: {
some: {
role: {
permissions: {
some: {
permission: { resource: recht.resource, action: recht.action },
},
},
},
},
},
},
});
if (traeger === 0) {
fehlend.push(`${recht.resource}:${recht.action} (${recht.wofuer})`);
}
}
if (fehlend.length === 0) return;
console.warn(
'\n' +
'========================================================================\n' +
' ACHTUNG: Für folgende Rechte gibt es KEIN aktives Konto:\n' +
fehlend.map((f) => ` ${f}\n`).join('') +
'\n' +
' Das ist kein Fehler der Anwendung, sondern eine Lücke in der\n' +
' Rechtevergabe und sie fällt erst auf, wenn eine Frist läuft.\n' +
'\n' +
' Beheben: In der Benutzerverwaltung bei einem verantwortlichen Konto\n' +
' den Haken „DSGVO-Zugriff" setzen (Audit-Protokoll lesen und\n' +
' Datenschutz-Verwaltung). Für Eingriffe am Protokoll versiegeln,\n' +
' aufräumen, Aufbewahrung ändern zusätzlich „Audit-Betrieb".\n' +
'========================================================================\n',
);
} catch (err) {
// Eine Wache darf den Start nicht verhindern. Aber schweigen darf sie
// auch nicht: „nicht geprüft" ist nicht dasselbe wie „nichts gefunden".
console.warn(
'[Pflichtrechte] Prüfung nicht möglich der Zustand der Rechtevergabe ist ' +
'damit UNBEKANNT, nicht in Ordnung:',
err instanceof Error ? err.message : err,
);
}
}