Der zweite Teil von #006 bindet den Kern an Datenbank und Worker. Nur src/worker/geheimnisse.ts liest Chiffrat und Data-Key; ein struktureller Test haelt fest, dass der Webprozess nichts von dort importiert und so nie an den Master-Key kommt. Die Zusatzdaten binden jedes Chiffrat an ID und Fingerprint seines Schluessels: kopiert man es in eine andere Zeile, laesst es sich dort nicht oeffnen. Der Deployment-Key entsteht beim ersten Start; ein Test prueft, dass ein pg_dump keinen Klartext enthaelt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
import { readFileSync, readdirSync, statSync } from "node:fs";
|
||
import path from "node:path";
|
||
import { describe, expect, it } from "vitest";
|
||
|
||
/**
|
||
* Strukturell (#006): Nur der Worker liest Chiffrat und Data-Key, und der Webprozess
|
||
* importiert nichts aus src/worker – sonst laege der Master-Key-Zugriff im Web.
|
||
*/
|
||
function dateien(verzeichnis: string): string[] {
|
||
return readdirSync(verzeichnis).flatMap((name) => {
|
||
const pfad = path.join(verzeichnis, name);
|
||
if (statSync(pfad).isDirectory()) return name === "generated" ? [] : dateien(pfad);
|
||
return /\.(ts|tsx)$/.test(name) ? [pfad.split(path.sep).join("/")] : [];
|
||
});
|
||
}
|
||
|
||
const ALLE = dateien("src");
|
||
|
||
/** Dateien, die die Namen tragen, ohne die Datenbank zu lesen – mit Begruendung. */
|
||
const NAMEN_ERLAUBT: Record<string, string> = {
|
||
"src/worker/geheimnisse.ts": "die eine Stelle, die liest",
|
||
"src/lib/krypto/umschlag.ts": "Feldname des Umschlags, rein rechnerisch",
|
||
"src/lib/protokoll-schwaerzen.ts": "Feldname in der Schwaerz-Liste",
|
||
};
|
||
|
||
describe("Grenze um die Private Keys", () => {
|
||
it("nur src/worker/geheimnisse.ts liest privatVerschluesselt und dekVerschluesselt", () => {
|
||
const verstoesse = ALLE.filter(
|
||
(datei) =>
|
||
!NAMEN_ERLAUBT[datei] && /privatVerschluesselt|dekVerschluesselt/.test(readFileSync(datei, "utf8")),
|
||
);
|
||
expect(verstoesse).toEqual([]);
|
||
});
|
||
|
||
it("nichts ausserhalb von src/worker importiert aus src/worker", () => {
|
||
const verstoesse = ALLE.filter(
|
||
(datei) => !datei.startsWith("src/worker/") && /from ["']@\/worker\//.test(readFileSync(datei, "utf8")),
|
||
);
|
||
expect(verstoesse).toEqual([]);
|
||
});
|
||
});
|