Files
sshkeyvault/src/server/services/protokoll.ts
T
florian.wildeandClaude Opus 5.5 364c514b04 feat(protokoll): Eintraege vor dem Schreiben schwaerzen (#005)
Details und Diff laufen durch schwaerzen(), damit kein Private Key und
kein Passwort ins unveraenderliche Protokoll geraet - was dort einmal
steht, laesst sich per Trigger nicht mehr entfernen.

Der Wortstamm privat ist dafuer zu breit: er schwaerzte auch Pfade und
Verwendungszaehler, die das Protokoll gerade zeigen soll. Jetzt greifen
nur Felder, die den Schluessel selbst tragen, und jeder PEM-Block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:30:51 +02:00

53 lines
2.1 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 type { Prisma } from "@/generated/prisma/client";
import type { ProtokollErgebnis } from "@/generated/prisma/enums";
import { prisma } from "@/lib/db";
import { schwaerzen } from "@/lib/protokoll-schwaerzen";
/**
* Schreibt einen Eintrag ins revisionssichere Protokoll (#005).
*
* Hash und Verkettung berechnet die Datenbank (Trigger), hier wird nur eingefuegt.
* `details` laeuft vor dem Schreiben durch `schwaerzen()` (#006), damit kein Private Key
* und kein Passwort hineingeraet – auch nicht aus Versehen ueber ein Fehlerobjekt.
*/
export type ProtokollEintrag = {
readonly akteur: { readonly id: string; readonly name: string } | "System";
readonly aktion: string;
readonly ergebnis: ProtokollErgebnis;
readonly hostName?: string | null;
readonly zielbenutzer?: string | null;
readonly fingerprint?: string | null;
readonly diff?: string | null;
readonly details?: Record<string, unknown>;
};
export async function protokollieren(
eintrag: ProtokollEintrag,
client: Prisma.TransactionClient = prisma,
): Promise<void> {
const akteur = eintrag.akteur === "System" ? { id: null, name: "System" } : eintrag.akteur;
await client.protokoll.create({
data: {
akteurId: akteur.id,
akteurName: akteur.name,
aktion: eintrag.aktion,
ergebnis: eintrag.ergebnis,
hostName: eintrag.hostName ?? null,
zielbenutzer: eintrag.zielbenutzer ?? null,
fingerprint: eintrag.fingerprint ?? null,
// Auch der Diff: er stammt aus Dateien auf den Hosts, und eine authorized_keys-Datei
// kann versehentlich einen eingefuegten Private Key enthalten.
diff: eintrag.diff ? (schwaerzen(eintrag.diff) as string) : null,
details: eintrag.details ? (schwaerzen(eintrag.details) as Prisma.InputJsonValue) : undefined,
},
});
}
/** Prueft die Hash-Kette in der Datenbank; liefert die erste fehlerhafte ID oder null. */
export async function protokollKettePruefen(): Promise<bigint | null> {
const [zeile] = await prisma.$queryRaw<
{ ergebnis: bigint | null }[]
>`SELECT protokoll_kette_pruefen() AS ergebnis`;
return zeile?.ergebnis ?? null;
}