Siegel ueber null Blaettern meldet nicht mehr "intakt"
Ein Bestandssiegel, das zum Zeitpunkt des Siegelns keinen Altbestand vorfand, ist rechnerisch tadellos und schuetzt nichts. Gemeldet wurde trotzdem "intakt" - formal richtig, aber es liest sich als Schutzzusage. Der Pentester hat Stagings Leersiegel genau so missverstanden und hielt es fuer zahnhaltig. Das ist der rote Faden im Kleinen: ein Signal, das beruhigt, wo nichts abgesichert ist. Deshalb ein eigener Zustand "leer" mit eigenem Text - gewertet wie "nicht_noetig", kippt `valid` also nicht, sagt aber auch nichts zu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -262,6 +262,10 @@ export async function verifyIntegrity(req: AuthRequest, res: Response) {
|
||||
? ' ⚠ Bestandssiegel ENTFERNT: Es liegen versiegelte Blattwerte vor, aber kein gültiger ' +
|
||||
'Siegel-Marker mehr. Der Marker wurde gelöscht oder unbrauchbar gemacht – Änderungen am ' +
|
||||
'Altbestand wären dadurch wieder unsichtbar. Das ist KEIN Normalzustand.'
|
||||
: result.backlogSealStatus === 'leer'
|
||||
? ' Hinweis: Es besteht ein Bestandssiegel, das aber NICHTS umschließt – ' +
|
||||
'zum Zeitpunkt des Siegelns gab es keine unsignierten Alteinträge. Es sichert ' +
|
||||
'also nichts ab. Das ist kein Fehler, aber auch keine Zusage.'
|
||||
: result.backlogSealStatus === 'nicht_noetig'
|
||||
? ' Ein Bestandssiegel wird hier nicht gebraucht: Es gibt keine unsignierten Alteinträge.'
|
||||
: result.backlogSealStatus === 'kein_siegel'
|
||||
|
||||
@@ -914,7 +914,7 @@ export async function verifyIntegrity(fromId?: number, toId?: number): Promise<{
|
||||
* `kein_siegel` = nie erstellt. `entfernt` = Blaetter vorhanden, aber kein
|
||||
* gueltiger Marker mehr – der Anker wurde herausgeloest (Pentest R174-01).
|
||||
*/
|
||||
backlogSealStatus: 'kein_siegel' | 'intakt' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
|
||||
backlogSealStatus: 'kein_siegel' | 'intakt' | 'leer' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
|
||||
/** Altbestands-Zeilen, deren Inhalt vom Siegel abweicht. */
|
||||
backlogTampered: number[];
|
||||
/** Gesiegelte Zeilen, die nicht mehr existieren – Beweismaterial entfernt. */
|
||||
@@ -1061,7 +1061,7 @@ export async function verifyIntegrity(fromId?: number, toId?: number): Promise<{
|
||||
const backlogMissing: number[] = [];
|
||||
// Luecken, die das Siegel als bereits vorhanden beglaubigt (siehe unten).
|
||||
const beglaubigteLuecken = new Set<number>();
|
||||
let backlogSealStatus: 'kein_siegel' | 'intakt' | 'gebrochen' | 'entfernt' | 'nicht_noetig' = 'kein_siegel';
|
||||
let backlogSealStatus: 'kein_siegel' | 'intakt' | 'leer' | 'gebrochen' | 'entfernt' | 'nicht_noetig' = 'kein_siegel';
|
||||
let backlogSealRoot: string | null = null;
|
||||
|
||||
const siegelKandidaten = await prisma.auditLog.findMany({
|
||||
@@ -1164,6 +1164,17 @@ export async function verifyIntegrity(fromId?: number, toId?: number): Promise<{
|
||||
? 'intakt'
|
||||
: 'gebrochen';
|
||||
|
||||
// Ein Siegel ueber NULL Blaettern ist rechnerisch tadellos und schuetzt
|
||||
// nichts: es gab zum Zeitpunkt des Siegelns keinen Altbestand. „intakt“
|
||||
// zu melden waere formal richtig und trotzdem irrefuehrend - es liest
|
||||
// sich als Schutzzusage. Der Pentester hat Stagings Leersiegel genau so
|
||||
// missverstanden, und das ist der rote Faden im Kleinen: ein Signal, das
|
||||
// beruhigt, wo nichts abgesichert ist. Eigener Zustand, gleiche Wertung
|
||||
// wie „nicht noetig“ - kein Befund, aber auch keine Zusage.
|
||||
if (backlogSealStatus === 'intakt' && blaetter.length === 0) {
|
||||
backlogSealStatus = 'leer';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Beglaubigte Alt-Luecken
|
||||
//
|
||||
@@ -1325,6 +1336,7 @@ export async function verifyIntegrity(fromId?: number, toId?: number): Promise<{
|
||||
// unsichtbar (R174-01).
|
||||
const siegelInOrdnung =
|
||||
backlogSealStatus === 'intakt' ||
|
||||
backlogSealStatus === 'leer' ||
|
||||
backlogSealStatus === 'kein_siegel' ||
|
||||
backlogSealStatus === 'nicht_noetig';
|
||||
|
||||
|
||||
@@ -97,6 +97,21 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
|
||||
## ✅ Erledigt
|
||||
|
||||
- [x] **🏷️ Siegel über null Blättern meldet nicht mehr „intakt"** (2026-08-26)
|
||||
- Ein Bestandssiegel, das zum Zeitpunkt des Siegelns keinen Altbestand
|
||||
vorfand, ist rechnerisch tadellos und schützt **nichts**. Gemeldet wurde
|
||||
trotzdem `intakt` – formal richtig, aber es liest sich als Schutzzusage.
|
||||
- Der Pentester hat Stagings Leersiegel genau so missverstanden und hielt es
|
||||
für zahnhaltig. Das ist der rote Faden im Kleinen: ein Signal, das
|
||||
beruhigt, wo nichts abgesichert ist.
|
||||
- Eigener Zustand `leer` mit eigenem Text („besteht ein Bestandssiegel, das
|
||||
aber NICHTS umschließt … kein Fehler, aber auch keine Zusage").
|
||||
Wertung wie `nicht_noetig`: kippt `valid` nicht.
|
||||
- Dateien: `backend/src/services/audit.service.ts`,
|
||||
`backend/src/controllers/auditLog.controller.ts`,
|
||||
`frontend/src/services/api.ts`,
|
||||
`frontend/src/pages/settings/AuditIntegrityCard.tsx`
|
||||
|
||||
- [x] **🔒 `audit:export` gatete nichts – Export hing an `audit:read`** (2026-08-26)
|
||||
- **Bei der Gegenprobe zur neuen Rolle `Gegenbuch` gefunden:** Ein Konto mit
|
||||
ausschließlich `audit:read` bekam auf `GET /audit-logs/export` **200**.
|
||||
|
||||
@@ -50,6 +50,7 @@ const AMPEL_TITEL: Record<Ampel, string> = {
|
||||
|
||||
const SIEGEL_TEXT: Record<IntegrityResult['backlogSealStatus'], string> = {
|
||||
intakt: 'versiegelt und intakt',
|
||||
leer: 'Siegel vorhanden, aber ohne Inhalt',
|
||||
kein_siegel: 'nicht versiegelt',
|
||||
nicht_noetig: 'nicht nötig (alles signiert)',
|
||||
gebrochen: 'GEBROCHEN',
|
||||
|
||||
@@ -1750,7 +1750,7 @@ export interface IntegrityResult {
|
||||
attestedGaps: number[];
|
||||
/** Signierte Zeilen, die ohne AUDIT_HMAC_KEY nicht pruefbar sind. */
|
||||
unverifiableEntries: number[];
|
||||
backlogSealStatus: 'kein_siegel' | 'intakt' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
|
||||
backlogSealStatus: 'kein_siegel' | 'intakt' | 'leer' | 'gebrochen' | 'entfernt' | 'nicht_noetig';
|
||||
backlogTampered: number[];
|
||||
backlogMissing: number[];
|
||||
backlogSealCount: number;
|
||||
|
||||
Reference in New Issue
Block a user