74 lines
2.7 KiB
JavaScript
74 lines
2.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { describe, it } from "vitest";
|
|
|
|
import {
|
|
formatAuditCascadeGuardResult,
|
|
migrationPath,
|
|
schemaPath,
|
|
verifyAuditCascadeGuard,
|
|
} from "./verify-audit-cascade-guard.mjs";
|
|
|
|
const hardenedSchema = readFileSync(schemaPath, "utf8");
|
|
const hardenedMigration = readFileSync(migrationPath, "utf8");
|
|
const packageJson = JSON.parse(readFileSync(path.resolve("package.json"), "utf8"));
|
|
|
|
function verifyWithOverrides({ schemaText = hardenedSchema, migrationSql = hardenedMigration } = {}) {
|
|
return verifyAuditCascadeGuard({ schemaText, migrationSql });
|
|
}
|
|
|
|
describe("verify-audit-cascade-guard", () => {
|
|
it("passes against the hardened Prisma schema and migration", () => {
|
|
const result = verifyWithOverrides();
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.deepEqual(result.failures, []);
|
|
assert.match(formatAuditCascadeGuardResult(result), /^PASS impersonation audit cascade guard/);
|
|
});
|
|
|
|
it("fails when an audit relation reintroduces onDelete Cascade", () => {
|
|
const result = verifyWithOverrides({
|
|
schemaText: hardenedSchema.replace("onDelete: SetNull)", "onDelete: Cascade)"),
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.deepEqual(
|
|
result.failures.map((failure) => failure.id),
|
|
["schema.actor.optional-set-null", "schema.relation.no-cascade"],
|
|
);
|
|
assert.match(formatAuditCascadeGuardResult(result), /onDelete: Cascade/);
|
|
});
|
|
|
|
it("fails when actor or target SQL columns are made NOT NULL", () => {
|
|
const result = verifyWithOverrides({
|
|
migrationSql: hardenedMigration
|
|
.replace('"actorUserId" TEXT,', '"actorUserId" TEXT NOT NULL,')
|
|
.replace('"targetUserId" TEXT,', '"targetUserId" TEXT NOT NULL,'),
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.deepEqual(
|
|
result.failures.map((failure) => failure.id),
|
|
["migration.actorUserId.nullable", "migration.targetUserId.nullable"],
|
|
);
|
|
});
|
|
|
|
it("fails when actor, target, or tenant foreign keys are missing ON DELETE SET NULL", () => {
|
|
const result = verifyWithOverrides({
|
|
migrationSql: hardenedMigration.replaceAll("ON DELETE SET NULL", "ON DELETE CASCADE"),
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.deepEqual(
|
|
result.failures.map((failure) => failure.id),
|
|
["migration.actorUserId.set-null", "migration.targetUserId.set-null", "migration.tenantId.set-null"],
|
|
);
|
|
assert.match(formatAuditCascadeGuardResult(result), /migration\.tenantId\.set-null/);
|
|
});
|
|
|
|
it("registers the package script for the guard command", () => {
|
|
assert.equal(packageJson.scripts["verify:audit-cascade-guard"], "node scripts/verify-audit-cascade-guard.mjs");
|
|
});
|
|
});
|