Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/db/seed/SeedService.pii.test.ts
T

90 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
/**
* PII / secret regression test for SeedService.
*
* SeedService creates the bootstrap admin user with a hard-coded
* default password. The legacy console.log statements printed raw
* email addresses; the bigger risk is the password constant leaking
* by accident if a future change passes `password` into a logger arg.
*
* Policy:
* - never log raw `email` — always emailFingerprint(email)
* - never log `password`, `passwordHash`, `adminPassword`, or
* anything looking like a credential
* - no live console.* calls remain
*/
describe("SeedService PII discipline", () => {
const SOURCE = readFileSync(resolve(__dirname, "SeedService.ts"), "utf8");
it("contains no live console.* call sites", () => {
const live = SOURCE.split("\n").filter((line, idx, all) => {
if (!/console\.(log|error|warn|info|debug)\s*\(/.test(line)) return false;
const trimmed = line.trimStart();
if (trimmed.startsWith("//") || trimmed.startsWith("*")) return false;
const prev = idx > 0 ? all[idx - 1].trim() : "";
if (/eslint-disable-next-line[^\n]*no-console/.test(prev)) return false;
return true;
});
expect(live).toEqual([]);
});
it("never passes raw email / password fields as own seedLogger arg keys", () => {
const pattern = /seedLogger\.(info|warn|error|debug)\s*\(\s*\{([\s\S]*?)\}/g;
const forbiddenKeys = [
"email",
"password",
"passwordHash",
"adminEmail",
"adminPassword",
"rawPassword",
"plainPassword",
"secret",
"token",
];
const offenders: string[] = [];
let match: RegExpExecArray | null;
while ((match = pattern.exec(SOURCE)) !== null) {
const objContent = match[2];
for (const key of forbiddenKeys) {
const keyRe = new RegExp(`(^|[\\s,{])${key}\\s*:`, "m");
if (keyRe.test(objContent)) offenders.push(`key "${key}" in ${match[0].slice(0, 100)}...`);
}
}
expect(offenders, `seedLogger must not log PII/secret fields: ${offenders.join("; ")}`).toEqual([]);
});
it("defines and uses emailFingerprint() for any email-related diagnostics", () => {
expect(SOURCE).toMatch(/function emailFingerprint\(/);
expect(SOURCE).toMatch(/createHash\(["']sha256["']\)/);
// Anywhere we mention "email" inside a seedLogger call must be via emailFingerprint(...)
// (Aliased as emailFingerprint key — never raw email value)
expect(SOURCE).toMatch(/emailFingerprint\(email\)/);
});
it("uses createScopedLogger('seed')", () => {
expect(SOURCE).toMatch(/createScopedLogger\(\s*["']seed["']\s*\)/);
});
it("does not interpolate DEFAULT_ADMIN_PASSWORD into any log message", () => {
// Hard-coded admin password constant exists in source; ensure it never
// ends up in a logger arg object or template string.
const lines = SOURCE.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (/seedLogger\.|`/.test(line) && /DEFAULT_ADMIN_PASSWORD/.test(line)) {
// Only legal use of the constant: assignment line like `password: DEFAULT_ADMIN_PASSWORD` in the seed-user record.
// It must NOT appear inside a logger arg or template literal.
if (/seedLogger\./.test(line)) {
throw new Error(`SeedService PII leak: DEFAULT_ADMIN_PASSWORD used in seedLogger call at line ${i + 1}: ${line.trim()}`);
}
if (/`[^`]*\$\{[^}]*DEFAULT_ADMIN_PASSWORD/.test(line)) {
throw new Error(`SeedService PII leak: DEFAULT_ADMIN_PASSWORD interpolated in template at line ${i + 1}: ${line.trim()}`);
}
}
}
});
});