Files
kuber/tests/server/audit-store.test.ts
2026-09-27 10:40:50 +00:00

93 lines
3.2 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import {
AUDIT_REDACTED,
MemoryAuditStore,
redactAuditValue,
} from "../../server/audit-store";
describe("audit store", () => {
test("recursively redacts secrets without changing the input", async () => {
const details = {
authorization: "Bearer visible-before-redaction",
nested: [{ password: "hunter2", note: "safe" }],
header: "Bearer another-secret",
};
const store = new MemoryAuditStore();
const event = await store.append({
actor: { username: "admin" },
action: "workspace.update",
workspaceId: "demo",
outcome: "success",
details,
});
expect(event.spec.details).toEqual({
authorization: AUDIT_REDACTED,
nested: [{ password: AUDIT_REDACTED, note: "safe" }],
header: AUDIT_REDACTED,
});
expect(details.nested[0]?.password).toBe("hunter2");
});
test("is append-only and returns defensive copies", async () => {
const store = new MemoryAuditStore();
const event = await store.append({
actor: { username: "admin" },
action: "workspace.create",
workspaceId: "demo",
outcome: "success",
});
event.spec.action = "tampered";
expect((await store.list("demo"))[0]?.spec.action).toBe("workspace.create");
expect(redactAuditValue({ api_key: "key", ordinary: "value" })).toEqual({
api_key: AUDIT_REDACTED,
ordinary: "value",
});
});
test("memory persistence retains only the newest 100 events", async () => {
const store = new MemoryAuditStore(
() => new Date("2026-01-01T00:00:00.000Z"),
(() => { let id = 0; return () => `event-${++id}`; })(),
);
for (let index = 0; index < 105; index++) {
await store.append({ actor: { username: "admin" }, action: `action-${index}`, outcome: "success" });
}
const events = await store.list();
expect(events).toHaveLength(100);
expect(events[0]?.spec.action).toBe("action-5");
expect(events.at(-1)?.spec.action).toBe("action-104");
});
test("redacts credential-bearing URL strings at value level", () => {
expect(
redactAuditValue("git clone https://alice:[email protected]/org/repo.git"),
).toBe("git clone https://[REDACTED]@github.com/org/repo.git");
});
test("redacts AWS access key IDs at value level", () => {
expect(
redactAuditValue("connection used AKIAIOSFODNN7EXAMPLE to attach volume"),
).toBe("connection used [REDACTED] to attach volume");
});
test("redacts OpenSSH / private key headers at value level", () => {
expect(
redactAuditValue(
"ssh key\n-----BEGIN OPENSSH PRIVATE KEY-----\nabc123\n-----END OPENSSH PRIVATE KEY-----",
),
).toBe(
"ssh key\n[REDACTED]\nabc123\n-----END OPENSSH PRIVATE KEY-----",
);
});
test("preserves ordinary URLs and arbitrary identifiers", () => {
expect(
redactAuditValue("deploy from https://registry.example.com/v2/app"),
).toBe("deploy from https://registry.example.com/v2/app");
expect(
redactAuditValue("user [email protected] recovered the AKIA-referencing doc"),
).toBe("user [email protected] recovered the AKIA-referencing doc");
expect(redactAuditValue("id abc-123-def")).toBe("id abc-123-def");
});
});