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>
115 lines
4.0 KiB
TypeScript
115 lines
4.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { GESCHWAERZT, schwaerzen } from "@/lib/protokoll-schwaerzen";
|
|
|
|
const KEY =
|
|
"-----BEGIN OPENSSH PRIVATE KEY-----\n" +
|
|
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n" +
|
|
"-----END OPENSSH PRIVATE KEY-----";
|
|
|
|
describe("schwaerzen", () => {
|
|
it("ersetzt geheime Felder in verschachtelten Objekten und Arrays", () => {
|
|
const eingabe = {
|
|
name: "backup",
|
|
privateKeyOpenssh: "irgendwas",
|
|
privatVerschluesselt: Buffer.from("x"),
|
|
einstellungen: {
|
|
passwort: "hunter2",
|
|
Password: "hunter2",
|
|
sshPassphrase: "abc",
|
|
client_secret: "s",
|
|
geheimnisWert: { tief: 1 },
|
|
accessToken: "t",
|
|
},
|
|
liste: [{ token: "t", erlaubt: "ja" }, "frei", 3],
|
|
};
|
|
expect(schwaerzen(eingabe)).toEqual({
|
|
name: "backup",
|
|
privateKeyOpenssh: GESCHWAERZT,
|
|
privatVerschluesselt: GESCHWAERZT,
|
|
einstellungen: {
|
|
passwort: GESCHWAERZT,
|
|
Password: GESCHWAERZT,
|
|
sshPassphrase: GESCHWAERZT,
|
|
client_secret: GESCHWAERZT,
|
|
geheimnisWert: GESCHWAERZT,
|
|
accessToken: GESCHWAERZT,
|
|
},
|
|
liste: [{ token: GESCHWAERZT, erlaubt: "ja" }, "frei", 3],
|
|
});
|
|
// Eingabe bleibt unberuehrt.
|
|
expect(eingabe.einstellungen.passwort).toBe("hunter2");
|
|
});
|
|
|
|
it("findet Private Keys in beliebigen Zeichenketten", () => {
|
|
const ergebnis = schwaerzen({ notiz: `vorher ${KEY} nachher`, tief: [[KEY]] }) as {
|
|
notiz: string;
|
|
tief: string[][];
|
|
};
|
|
expect(ergebnis.notiz).toBe(`vorher ${GESCHWAERZT} nachher`);
|
|
expect(ergebnis.tief[0]![0]).toBe(GESCHWAERZT);
|
|
expect(JSON.stringify(ergebnis)).not.toContain("b3BlbnNzaC1rZXkt");
|
|
});
|
|
|
|
it("schwärzt Keys in Fehlermeldungen und Stacktraces", () => {
|
|
const fehler = new Error(`Parser scheiterte an ${KEY}`, { cause: { passphrase: "x", text: KEY } });
|
|
const ergebnis = schwaerzen({ fehler }) as { fehler: Record<string, unknown> };
|
|
expect(ergebnis.fehler["name"]).toBe("Error");
|
|
expect(ergebnis.fehler["message"]).toBe(`Parser scheiterte an ${GESCHWAERZT}`);
|
|
expect(JSON.stringify(ergebnis)).not.toContain("b3BlbnNzaC1rZXkt");
|
|
expect(JSON.stringify(ergebnis)).not.toContain("PRIVATE KEY");
|
|
expect(ergebnis.fehler["cause"]).toEqual({ passphrase: GESCHWAERZT, text: GESCHWAERZT });
|
|
});
|
|
|
|
it("schwärzt auch abgeschnittene Keys ohne END-Zeile", () => {
|
|
const abgeschnitten = KEY.slice(0, 60);
|
|
expect(schwaerzen(`log: ${abgeschnitten}`)).toBe(`log: ${GESCHWAERZT}`);
|
|
});
|
|
|
|
it("schwärzt andere PEM-Private-Keys (PKCS#1, PKCS#8)", () => {
|
|
for (const art of ["RSA PRIVATE KEY", "PRIVATE KEY", "ENCRYPTED PRIVATE KEY"]) {
|
|
const pem = `-----BEGIN ${art}-----\nMIIEow\n-----END ${art}-----`;
|
|
expect(schwaerzen(pem)).toBe(GESCHWAERZT);
|
|
}
|
|
});
|
|
|
|
it("lässt Public Keys und harmlose Werte stehen", () => {
|
|
const wert = {
|
|
publicKeyZeile: "ssh-ed25519 AAAAC3Nza x",
|
|
fingerprint: "SHA256:abc",
|
|
anzahl: 3,
|
|
aktiv: true,
|
|
leer: null,
|
|
zeit: new Date(0),
|
|
};
|
|
expect(schwaerzen(wert)).toEqual(wert);
|
|
expect(schwaerzen("-----BEGIN CERTIFICATE-----")).toBe("-----BEGIN CERTIFICATE-----");
|
|
});
|
|
|
|
it("ersetzt rohe Bytes und übersteht Zyklen", () => {
|
|
const zyklisch: Record<string, unknown> = { daten: Buffer.from("geheim") };
|
|
zyklisch["selbst"] = zyklisch;
|
|
expect(schwaerzen(zyklisch)).toEqual({ daten: "[Binärdaten]", selbst: "[zirkulär]" });
|
|
});
|
|
|
|
it("zeigt mehrfach referenzierte Teilbäume vollständig", () => {
|
|
const geteilt = { a: 1 };
|
|
expect(schwaerzen({ x: geteilt, y: geteilt })).toEqual({ x: { a: 1 }, y: { a: 1 } });
|
|
});
|
|
});
|
|
|
|
describe("harmlose Felder mit privat im Namen (#005)", () => {
|
|
it("bleiben lesbar", () => {
|
|
expect(
|
|
schwaerzen({
|
|
privateVerwendungen: 2,
|
|
privatePfad: "~/.ssh/id_ed25519_x",
|
|
privatGeloeschtAm: "2026-10-12",
|
|
}),
|
|
).toEqual({
|
|
privateVerwendungen: 2,
|
|
privatePfad: "~/.ssh/id_ed25519_x",
|
|
privatGeloeschtAm: "2026-10-12",
|
|
});
|
|
});
|
|
});
|