Make audit events immutable
This commit is contained in:
1 parent
20c8ef6ef3
commit
689fe861f1
4 files changed
+41
-1
No files matched your search
@@ -0,0 +1,16 @@
|
||||
export const auditEventImmutabilitySchema = `
|
||||
create trigger if not exists trg_audit_events_no_update
|
||||
before update on audit_events
|
||||
begin
|
||||
select raise(abort, 'Audit events are immutable.');
|
||||
end;
|
||||
|
||||
create trigger if not exists trg_audit_events_no_delete
|
||||
before delete on audit_events
|
||||
begin
|
||||
select raise(abort, 'Audit events are immutable.');
|
||||
end;
|
||||
|
||||
insert into schema_migrations(version, name, applied_at)
|
||||
values (33, 'audit_event_immutability', datetime('now'));
|
||||
`;
|
||||
@@ -31,6 +31,7 @@ import { fileHandoffRecordsSchema } from "./migrations/029-file-handoff-records"
|
||||
import { approvalSourceRefsSchema } from "./migrations/030-approval-source-refs";
|
||||
import { providerRequestWorkspaceSchema } from "./migrations/031-provider-request-workspace";
|
||||
import { browserActionMetadataSchema } from "./migrations/032-browser-action-metadata";
|
||||
import { auditEventImmutabilitySchema } from "./migrations/033-audit-event-immutability";
|
||||
|
||||
interface Migration {
|
||||
version: number;
|
||||
@@ -71,6 +72,7 @@ const MIGRATIONS: Migration[] = [
|
||||
{ version: 30, name: "approval_source_refs", sql: approvalSourceRefsSchema },
|
||||
{ version: 31, name: "provider_request_workspace", sql: providerRequestWorkspaceSchema },
|
||||
{ version: 32, name: "browser_action_metadata", sql: browserActionMetadataSchema },
|
||||
{ version: 33, name: "audit_event_immutability", sql: auditEventImmutabilitySchema },
|
||||
];
|
||||
|
||||
const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
||||
|
||||
@@ -13,6 +13,28 @@ function createConnection(): DatabaseConnection {
|
||||
}
|
||||
|
||||
describe("audit service", () => {
|
||||
test("keeps core audit events immutable", () => {
|
||||
const connection = createConnection();
|
||||
const organization = new OrganizationService(connection.db);
|
||||
let auditId = "";
|
||||
|
||||
connection.transaction(() => {
|
||||
const orgId = organization.createOrganization({
|
||||
organizationName: "Acme Operations",
|
||||
employeeName: "Dana Lee",
|
||||
mission: "Coordinate agents with auditable approvals.",
|
||||
goal: "Ship a verified control plane.",
|
||||
});
|
||||
auditId = (connection.db.prepare("select id from audit_events where org_id = ? limit 1").get(orgId) as { id: string }).id;
|
||||
});
|
||||
|
||||
expect(() => connection.db.prepare("update audit_events set action = ? where id = ?")
|
||||
.run("changed", auditId)).toThrow("Audit events are immutable.");
|
||||
expect(() => connection.db.prepare("delete from audit_events where id = ?")
|
||||
.run(auditId)).toThrow("Audit events are immutable.");
|
||||
connection.close();
|
||||
});
|
||||
|
||||
test("creates thread scoped replay runs from immutable audit events", () => {
|
||||
const connection = createConnection();
|
||||
const organization = new OrganizationService(connection.db);
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("domain services", () => {
|
||||
const connection = createConnection();
|
||||
|
||||
const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>;
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]);
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33]);
|
||||
connection.close();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user