Integritaetsstatus des Audit-Protokolls in der Oberflaeche

Der Zustand der Hash-Kette war bisher nur per POST /api/audit-logs/verify
einsehbar - also praktisch nur fuer das Gegenbuch und fuer jemanden mit
curl. Jetzt steht er oben auf Einstellungen -> Audit-Protokoll.

Vier Zustaende statt gruen/rot: unversehrt; Befund; unversehrt aber
ungeschuetzter Altbestand (kein_siegel); nicht vollstaendig pruefbar
(signierte Zeilen ohne AUDIT_HMAC_KEY). Die beiden mittleren sind
bewusst nicht gruen - ein Protokoll mit unversiegeltem Altbestand ist
rechnerisch stimmig, aber am Altbestand unbemerkt aenderbar, und ein
Protokoll, das mangels Schluessel nicht pruefbar ist, ist schlicht
ungeprueft. Beides als "alles in Ordnung" zu zeigen waere genau die
Klasse Fehler, die diese Runde behandelt hat. Schlaegt die Pruefung
selbst fehl, steht dort ausdruecklich, dass das keine Entwarnung ist.

Aufklappbare Einzelheiten trennen die unterschiedlich schweren
Kategorien: nachtraeglich veraendert (ernst) / Verkettung unterbrochen /
davon ohne dokumentierte Loeschung / davon vom Siegel beglaubigt / ohne
Schluessel nicht pruefbar - mit Erklaerung im Klartext.

Die Pruefung liest die gesamte Kette; sie laeuft daher einmal beim
Oeffnen der Seite und wird 5 Minuten wiederverwendet. Bewusst read-only:
kein Siegel- oder Rehash-Knopf, denn diese Eingriffe verlangen
audit:admin und eine ausdrueckliche Bestaetigung und gehoeren nicht neben
eine Statusanzeige, die man im Vorbeigehen anklickt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 09:11:13 +02:00
co-authored by Claude Opus 5
parent ecaeae48d4
commit 7af6b7591b
4 changed files with 232 additions and 1 deletions
+25 -1
View File
@@ -1734,6 +1734,30 @@ export interface AuditLogSearchParams {
search?: string;
}
/** Ergebnis der Integritaetspruefung des Audit-Protokolls. */
export interface IntegrityResult {
valid: boolean;
checkedCount: number;
/** Noch offene Beanstandungen nur hieran haengt `valid`. */
invalidEntries: number[];
/** ERNST: Inhalt einer bestehenden Zeile wurde nachtraeglich veraendert. */
tamperedEntries: number[];
/** Verkettung unterbrochen; die Zeilen selbst koennen unversehrt sein. */
chainGaps: number[];
/** Luecken ohne protokolliertes Loeschungs-Manifest. */
unexplainedGaps: number[];
/** Alt-Luecken, die das Bestandssiegel als bereits vorhanden beglaubigt. */
attestedGaps: number[];
/** Signierte Zeilen, die ohne AUDIT_HMAC_KEY nicht pruefbar sind. */
unverifiableEntries: number[];
backlogSealStatus: 'kein_siegel' | 'intakt' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
backlogTampered: number[];
backlogMissing: number[];
backlogSealCount: number;
tampered: boolean;
message: string;
}
export const auditLogApi = {
search: async (params?: AuditLogSearchParams) => {
const res = await api.get<ApiResponse<AuditLog[]>>('/audit-logs', { params });
@@ -1752,7 +1776,7 @@ export const auditLogApi = {
return res.data;
},
verifyIntegrity: async () => {
const res = await api.post<ApiResponse<{ valid: boolean; checkedCount: number; invalidEntries: number[]; tamperedEntries: number[]; chainGaps: number[]; unexplainedGaps: number[]; attestedGaps: number[]; unverifiableEntries: number[]; tampered: boolean; message: string }>>('/audit-logs/verify');
const res = await api.post<ApiResponse<IntegrityResult>>('/audit-logs/verify');
return res.data;
},
rehash: async () => {