93 lines
3.2 KiB
TypeScript
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");
|
|
});
|
|
});
|