openclaw / src /audit /execution-identity-context.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
34810d2 verified
Raw History Blame Contribute Delete
37.3 kB
import { afterEach, describe, expect, it } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
import {
insertOperatorApproval,
resolveOperatorApproval,
} from "../gateway/operator-approval-store.js";
import { openNodeSqliteDatabase } from "../infra/node-sqlite.js";
import {
closeOpenClawStateDatabaseForTest,
openOpenClawStateDatabase,
type OpenClawStateDatabase,
} from "../state/openclaw-state-db.js";
import { recordAuditEvent } from "./audit-event-store.js";
import {
configureExecutionIdentityAdmissionSink,
createExecutionIdentityAdmissionToken,
enqueueExecutionIdentityContextAtAdmission,
type ExecutionIdentityAdmissionEnvelope,
type ExecutionIdentityAdmissionFacts,
} from "./execution-identity-admission.js";
import {
inspectExecutionIdentityRun,
processExecutionIdentityAdmissionWork,
pruneExpiredExecutionIdentityContexts,
} from "./execution-identity-context.js";
const RETENTION_MS = 30 * 24 * 60 * 60_000;
afterEach(() => {
closeOpenClawStateDatabaseForTest();
});
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
function databaseOptions() {
return { env: { OPENCLAW_STATE_DIR: tempDirs.make("openclaw-identity-") } };
}
function openIndependentStateDatabase(path: string): OpenClawStateDatabase {
return {
db: openNodeSqliteDatabase(path),
path,
walMaintenance: { checkpoint: () => true, close: () => true },
};
}
function facts(
runId: string,
overrides: Partial<ExecutionIdentityAdmissionFacts> = {},
): ExecutionIdentityAdmissionFacts {
return {
runId,
agentId: "main",
ingress: { kind: "local-cli", boundary: "agent-command.local", state: "present" },
runtime: { kind: "embedded" },
...overrides,
};
}
function captureExecutionIdentityAdmissionEnvelope(
admissionFacts: ExecutionIdentityAdmissionFacts,
options: {
now?: number;
contextId?: string;
executionId?: string;
runtimeInstanceId?: string;
} = {},
): ExecutionIdentityAdmissionEnvelope {
const { contextId, executionId, runtimeInstanceId, now } = options;
let envelope: ExecutionIdentityAdmissionEnvelope | undefined;
const clear = configureExecutionIdentityAdmissionSink((captured) => {
if (captured.kind === "capture") {
envelope = captured.envelope;
}
return true;
});
try {
const result = enqueueExecutionIdentityContextAtAdmission(admissionFacts, {
enabled: true,
...(contextId !== undefined ? { contextId } : {}),
...(executionId !== undefined ? { executionId } : {}),
...(runtimeInstanceId !== undefined ? { runtimeInstanceId } : {}),
...(now !== undefined ? { now } : {}),
});
if (!result || !envelope) {
throw new Error("expected admission envelope");
}
return envelope;
} finally {
clear();
}
}
function persistExecutionIdentityAdmissionEnvelope(
envelope: ExecutionIdentityAdmissionEnvelope,
options: Parameters<typeof processExecutionIdentityAdmissionWork>[1] = {},
) {
return processExecutionIdentityAdmissionWork({ kind: "capture", envelope }, options);
}
function prepareExecutionIdentityContextAtAdmission(
admissionFacts: ExecutionIdentityAdmissionFacts,
options: {
database?: OpenClawStateDatabase;
env?: NodeJS.ProcessEnv;
now?: number;
contextId?: string;
executionId?: string;
runtimeInstanceId?: string;
limits?: { maxRows: number; pruneBatchRows: number };
} = {},
) {
const { contextId, executionId, runtimeInstanceId, now, limits, ...database } = options;
const envelope = captureExecutionIdentityAdmissionEnvelope(admissionFacts, {
...(contextId !== undefined ? { contextId } : {}),
...(executionId !== undefined ? { executionId } : {}),
...(runtimeInstanceId !== undefined ? { runtimeInstanceId } : {}),
...(now !== undefined ? { now } : {}),
});
return persistExecutionIdentityAdmissionEnvelope(envelope, {
...database,
...(now !== undefined ? { now } : {}),
...(limits !== undefined ? { limits } : {}),
});
}
function recordDeniedApprovalForRun(
runId: string,
database: ReturnType<typeof databaseOptions>,
id = "denied-approval",
binding?: { contextId: string; executionId: string },
): void {
insertOperatorApproval({
approval: {
id,
kind: "exec",
presentation: {
kind: "exec",
commandText: "details withheld",
allowedDecisions: ["allow-once", "deny"],
},
source: { runId, toolCallId: "private-tool-call", toolName: "exec" },
runtimeEpoch: "runtime-1",
createdAtMs: 100,
expiresAtMs: 1_000,
...(binding
? {
executionIdentityToken: {
tokenVersion: 1,
createdAt: 100,
runId,
contextId: binding.contextId,
executionId: binding.executionId,
},
}
: {}),
},
databaseOptions: database,
});
resolveOperatorApproval({
id,
decision: "deny",
resolver: { kind: "device", id: "private-reviewer-device" },
nowMs: 200,
databaseOptions: database,
});
}
describe("execution identity context storage", () => {
it("replays one byte-identical canonical context idempotently across restart", () => {
const database = databaseOptions();
const envelope = captureExecutionIdentityAdmissionEnvelope(facts("run-1"), {
now: 100,
contextId: "context-1",
executionId: "execution-1",
runtimeInstanceId: "runtime-secret-1",
});
const first = persistExecutionIdentityAdmissionEnvelope(envelope, { ...database, now: 100 });
closeOpenClawStateDatabaseForTest();
const second = persistExecutionIdentityAdmissionEnvelope(structuredClone(envelope), {
...database,
now: 999,
});
expect(second).toEqual(first);
expect(first.coverageState).toBe("unattributed");
expect(first.invoker).toEqual({ state: "absent" });
expect(Object.isFrozen(first)).toBe(true);
expect(Object.isFrozen(first.runtimeInstance)).toBe(true);
expect(JSON.stringify(first)).not.toContain("runtime-secret-1");
closeOpenClawStateDatabaseForTest();
const afterRestart = inspectExecutionIdentityRun(
{ executionId: "execution-1" },
{
...database,
now: 999,
},
);
expect(afterRestart.identity).toEqual({ state: "present", context: first });
});
it("keeps explicit unknown invoker evidence distinct from omission across restart", () => {
const database = databaseOptions();
const unknown = prepareExecutionIdentityContextAtAdmission(
facts("run-unknown", { invoker: { state: "unknown" } }),
{
...database,
now: 100,
contextId: "context-unknown",
executionId: "execution-unknown",
runtimeInstanceId: "runtime-unknown",
},
);
const absent = prepareExecutionIdentityContextAtAdmission(facts("run-absent"), {
...database,
now: 101,
contextId: "context-absent",
executionId: "execution-absent",
runtimeInstanceId: "runtime-absent",
});
expect(unknown).toMatchObject({
invoker: { state: "unknown" },
coverageState: "unknown",
missingEvidence: ["invoker.principal"],
});
expect(unknown.invoker).not.toHaveProperty("principal");
expect(absent).toMatchObject({
invoker: { state: "absent" },
coverageState: "unattributed",
missingEvidence: ["invoker.principal"],
});
closeOpenClawStateDatabaseForTest();
const unknownAfterRestart = inspectExecutionIdentityRun(
{ executionId: "execution-unknown" },
{ ...database, now: 101 },
);
expect(unknownAfterRestart.identity).toEqual({ state: "present", context: unknown });
expect(unknownAfterRestart.coverage).toEqual({
state: "unknown",
missingEvidence: ["invoker.principal"],
});
expect(unknownAfterRestart.decisions).toEqual([
expect.objectContaining({
enforcement: expect.objectContaining({ coverageState: "unknown" }),
missingEvidence: ["invoker.principal"],
}),
]);
expect(
inspectExecutionIdentityRun({ executionId: "execution-absent" }, { ...database, now: 101 })
.identity,
).toEqual({ state: "present", context: absent });
});
it.each([
{
difference: "contextId",
mutate: (envelope: ExecutionIdentityAdmissionEnvelope) => ({
...envelope,
contextId: "context-conflicting",
}),
},
{
difference: "createdAt",
mutate: (envelope: ExecutionIdentityAdmissionEnvelope) => ({
...envelope,
createdAt: envelope.createdAt + 1,
}),
},
{
difference: "identity facts",
mutate: (envelope: ExecutionIdentityAdmissionEnvelope) => ({
...envelope,
agentId: "other",
}),
},
])(
"conflicts on a same-execution $difference and leaves canonical bytes unchanged",
({ mutate }) => {
const database = databaseOptions();
const envelope = captureExecutionIdentityAdmissionEnvelope(facts("run-conflict"), {
contextId: "context-original",
executionId: "execution-original",
now: 100,
runtimeInstanceId: "runtime-1",
});
const original = persistExecutionIdentityAdmissionEnvelope(envelope, {
...database,
now: 100,
});
const originalRow = openOpenClawStateDatabase(database)
.db.prepare("SELECT context_json FROM execution_identity_contexts WHERE execution_id = ?")
.get("execution-original");
expect(() =>
persistExecutionIdentityAdmissionEnvelope(mutate(envelope), { ...database, now: 101 }),
).toThrow("execution identity context conflict");
expect(
openOpenClawStateDatabase(database)
.db.prepare("SELECT context_json FROM execution_identity_contexts WHERE execution_id = ?")
.get("execution-original"),
).toEqual(originalRow);
expect(
inspectExecutionIdentityRun(
{ executionId: "execution-original" },
{ ...database, now: 101 },
).identity,
).toEqual({ state: "present", context: original });
},
);
it("keeps distinct turns sharing one run correlation exactly inspectable", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("session-run"), {
...database,
now: 100,
contextId: "context-first",
executionId: "execution-first",
runtimeInstanceId: "runtime-1",
});
prepareExecutionIdentityContextAtAdmission(facts("session-run"), {
...database,
now: 101,
contextId: "context-second",
executionId: "execution-second",
runtimeInstanceId: "runtime-1",
});
recordDeniedApprovalForRun("session-run", database, "shared-run-approval", {
contextId: "context-first",
executionId: "execution-first",
});
const discovery = inspectExecutionIdentityRun(
{ runId: "session-run" },
{ ...database, now: 300 },
);
expect(discovery).toMatchObject({
run: { runId: "session-run", status: "known" },
identity: {
state: "ambiguous",
reasonCode: "execution_selection_required",
candidates: [
{ executionId: "execution-first", contextId: "context-first" },
{ executionId: "execution-second", contextId: "context-second" },
],
},
decisions: [],
});
for (const [executionOffset, executionId, nextExecutionCursor] of [
[0, "execution-first", "1"],
[1, "execution-second", undefined],
] as const) {
expect(
inspectExecutionIdentityRun(
{ runId: "session-run", executionOffset, executionLimit: 1 },
{ ...database, now: 300 },
),
).toMatchObject({
identity: { state: "ambiguous", candidates: [{ executionId }] },
...(nextExecutionCursor ? { nextExecutionCursor } : {}),
});
}
const firstInspection = inspectExecutionIdentityRun(
{ executionId: "execution-first" },
{ ...database, now: 300 },
);
const secondInspection = inspectExecutionIdentityRun(
{ executionId: "execution-second" },
{ ...database, now: 300 },
);
expect(firstInspection).toMatchObject({
identity: { state: "present", context: { contextId: "context-first" } },
coverage: { state: "enforced" },
decisions: [{ decision: { outcome: "not-applicable" } }, { decision: { outcome: "denied" } }],
});
expect(secondInspection).toMatchObject({
identity: { state: "present", context: { contextId: "context-second" } },
coverage: {
state: "unknown",
missingEvidence: expect.arrayContaining(["decision.execution_link"]),
},
decisions: [
{ decision: { outcome: "not-applicable" } },
{
decision: {
outcome: "unknown",
reasonCode: "operator_approval_execution_link_mismatch",
},
},
],
});
});
it("confirms durable retries without manufacturing lost evidence", () => {
const database = databaseOptions();
const envelope = captureExecutionIdentityAdmissionEnvelope(facts("run-recovery"), {
now: 100,
contextId: "context-recovery",
executionId: "execution-recovery",
runtimeInstanceId: "runtime-original",
});
const original = processExecutionIdentityAdmissionWork(
{ kind: "capture", envelope },
{ ...database, now: 100 },
);
const token = createExecutionIdentityAdmissionToken("run-recovery", {
now: 100,
contextId: "context-recovery",
executionId: "execution-recovery",
});
closeOpenClawStateDatabaseForTest();
expect(
processExecutionIdentityAdmissionWork({ kind: "retry-reference", token }, database),
).toEqual(original);
const missingDatabase = databaseOptions();
expect(() =>
processExecutionIdentityAdmissionWork({ kind: "retry-reference", token }, missingDatabase),
).toThrow("execution identity recovery evidence unavailable");
expect(
inspectExecutionIdentityRun({ executionId: "execution-recovery" }, missingDatabase),
).toMatchObject({
run: { executionId: "execution-recovery", status: "unknown" },
identity: { state: "unknown", reasonCode: "execution_not_found" },
decisions: [],
});
});
it("projects authoritative local CLI and system ingress without conflating them", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-local"), database);
prepareExecutionIdentityContextAtAdmission(
facts("run-system", {
ingress: { kind: "system", boundary: "gateway.boot", state: "present" },
}),
database,
);
expect(inspectExecutionIdentityRun({ runId: "run-local" }, database).identity).toMatchObject({
state: "present",
context: {
ingress: { kind: "local-cli", boundary: "agent-command.local", state: "present" },
},
});
expect(inspectExecutionIdentityRun({ runId: "run-system" }, database).identity).toMatchObject({
state: "present",
context: {
ingress: { kind: "system", boundary: "gateway.boot", state: "present" },
},
});
});
it("keeps inspection read-only and lets persistence create the additive table", () => {
const database = databaseOptions();
const reopened = openOpenClawStateDatabase(database);
expect(
reopened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toBeUndefined();
expect(inspectExecutionIdentityRun({ runId: "missing" }, database)).toMatchObject({
run: { status: "unknown" },
identity: { state: "unknown", reasonCode: "run_not_found" },
});
expect(
reopened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toBeUndefined();
prepareExecutionIdentityContextAtAdmission(facts("schema-restored"), database);
expect(
reopened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toEqual({ name: "execution_identity_contexts" });
expect(
reopened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'index' AND name = ?")
.get("execution_identity_contexts_run_created_idx"),
).toEqual({ name: "execution_identity_contexts_run_created_idx" });
});
it("keeps maintenance read-only until the first identity capture", () => {
const database = databaseOptions();
const opened = openOpenClawStateDatabase(database);
expect(
opened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toBeUndefined();
expect(pruneExpiredExecutionIdentityContexts({ database })).toBe(0);
expect(
opened.db
.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toBeUndefined();
});
it("records attribution only when an invoker fact is actually present", () => {
const database = databaseOptions();
const context = prepareExecutionIdentityContextAtAdmission(
facts("run-attributed", {
invoker: {
state: "present",
kind: "local-account",
rawPrincipalRef: "private-local-account",
displayLabel: "Operator OPENAI_API_KEY=sk-1234567890abcdef",
},
applicableGrants: [
{ rawGrantRef: "grant-z", state: "present" },
{ rawGrantRef: "grant-a", state: "present" },
],
assurance: [
{
kind: "local-process",
rawEvidenceRef: "private-process-evidence",
strength: "boundary-verified",
},
],
}),
{ ...database, runtimeInstanceId: "private-runtime" },
);
const encoded = JSON.stringify(context);
expect(context.coverageState).toBe("attribution-only");
expect(context.invoker.state).toBe("present");
expect(context.missingEvidence).toEqual([]);
expect(context.applicableGrants.map((grant) => grant.grantRef)).toEqual(
context.applicableGrants.map((grant) => grant.grantRef).toSorted(),
);
for (const secret of [
"private-local-account",
"private-process-evidence",
"private-runtime",
"grant-a",
"grant-z",
"sk-1234567890abcdef",
]) {
expect(encoded).not.toContain(secret);
}
expect(context.invoker.principal?.principalRef).toMatch(/^hmac-sha256:v1:/u);
});
it("declines recording instead of rotating a missing HMAC key with retained contexts", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-before-key-loss"), database);
openOpenClawStateDatabase(database).db.exec("DELETE FROM audit_identity_keys;");
closeOpenClawStateDatabaseForTest();
expect(() =>
prepareExecutionIdentityContextAtAdmission(facts("run-after-key-loss"), database),
).toThrow("audit identity key is missing");
});
it("skips new context rows when audit collection is disabled", () => {
const database = databaseOptions();
expect(
enqueueExecutionIdentityContextAtAdmission(facts("run-disabled"), { enabled: false }),
).toBeUndefined();
expect(
openOpenClawStateDatabase(database)
.db.prepare("SELECT name FROM sqlite_schema WHERE type = 'table' AND name = ?")
.get("execution_identity_contexts"),
).toBeUndefined();
});
it("keeps bounded retention maintenance available while collection is disabled", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-before-disable"), {
...database,
now: 0,
runtimeInstanceId: "runtime-1",
});
expect(
enqueueExecutionIdentityContextAtAdmission(facts("run-disabled"), { enabled: false }),
).toBeUndefined();
expect(pruneExpiredExecutionIdentityContexts({ database, now: RETENTION_MS + 1 })).toBe(1);
expect(
openOpenClawStateDatabase(database)
.db.prepare("SELECT COUNT(*) AS count FROM execution_identity_contexts")
.get(),
).toEqual({ count: 0 });
});
it("leaves the original context intact when a later worker write conflicts", () => {
const database = databaseOptions();
const envelope = captureExecutionIdentityAdmissionEnvelope(facts("run-best-effort"), {
contextId: "context-best-effort",
executionId: "execution-best-effort",
runtimeInstanceId: "runtime-1",
});
persistExecutionIdentityAdmissionEnvelope(envelope, database);
expect(() =>
persistExecutionIdentityAdmissionEnvelope(
{ ...envelope, agentId: "conflicting-agent" },
database,
),
).toThrow("execution identity context conflict");
});
it("rolls back the worker write when insert-time retention cleanup fails", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-expired"), {
...database,
now: 0,
runtimeInstanceId: "runtime-1",
});
openOpenClawStateDatabase(database).db.exec(`
CREATE TRIGGER reject_identity_cleanup
BEFORE DELETE ON execution_identity_contexts
BEGIN
SELECT RAISE(ABORT, 'cleanup unavailable');
END;
`);
expect(() =>
prepareExecutionIdentityContextAtAdmission(facts("run-still-admitted"), {
...database,
now: RETENTION_MS + 1,
runtimeInstanceId: "runtime-1",
}),
).toThrow("cleanup unavailable");
});
it("stops projecting context and decisions immediately after the retention boundary", () => {
const database = databaseOptions();
const createdAt = 1_000;
prepareExecutionIdentityContextAtAdmission(facts("run-retention"), {
...database,
now: createdAt,
contextId: "expired-context-secret",
executionId: "expired-execution-secret",
runtimeInstanceId: "expired-runtime-secret",
});
const immediatelyBefore = inspectExecutionIdentityRun(
{ runId: "run-retention" },
{ ...database, now: createdAt + RETENTION_MS - 1 },
);
expect(immediatelyBefore.identity).toMatchObject({
state: "present",
context: { contextId: "expired-context-secret" },
});
expect(immediatelyBefore.decisions).toHaveLength(1);
expect(
inspectExecutionIdentityRun(
{ runId: "run-retention" },
{ ...database, now: createdAt + RETENTION_MS },
).identity.state,
).toBe("present");
const immediatelyAfter = inspectExecutionIdentityRun(
{ runId: "run-retention" },
{ ...database, now: createdAt + RETENTION_MS + 1 },
);
expect(immediatelyAfter).toMatchObject({
run: { status: "known" },
identity: {
state: "unsupported",
reasonCode: "identity_context_unavailable",
remediation: [
expect.objectContaining({
code: "run_again_after_expiry",
text: expect.stringContaining("outside the 30-day window"),
}),
],
},
decisions: [],
coverage: { state: "unsupported", missingEvidence: ["identity.context"] },
});
expect(JSON.stringify(immediatelyAfter)).not.toContain("expired-context-secret");
expect(JSON.stringify(immediatelyAfter)).not.toContain("expired-runtime-secret");
expect(JSON.stringify(immediatelyAfter)).not.toContain("run_admission_identity_not_evaluated");
const exactAfter = inspectExecutionIdentityRun(
{ executionId: "expired-execution-secret" },
{ ...database, now: createdAt + RETENTION_MS + 1 },
);
expect(exactAfter).toMatchObject({
run: { executionId: "expired-execution-secret", status: "known" },
identity: { state: "unsupported", reasonCode: "identity_context_unavailable" },
decisions: [],
});
expect(JSON.stringify(exactAfter)).not.toContain("expired-context-secret");
expect(JSON.stringify(exactAfter)).not.toContain("expired-runtime-secret");
closeOpenClawStateDatabaseForTest();
expect(
inspectExecutionIdentityRun(
{ runId: "run-retention" },
{ ...database, now: createdAt + RETENTION_MS + 1 },
),
).toEqual(immediatelyAfter);
expect(
pruneExpiredExecutionIdentityContexts({
database,
now: createdAt + RETENTION_MS + 1,
}),
).toBe(1);
expect(
inspectExecutionIdentityRun(
{ runId: "run-retention" },
{ ...database, now: createdAt + RETENTION_MS + 1 },
),
).toMatchObject({
run: { status: "unknown" },
identity: {
state: "unknown",
reasonCode: "run_not_found",
remediation: [
expect.objectContaining({ text: expect.stringContaining("not proof of no run") }),
],
},
decisions: [],
});
});
it("prunes expired contexts in bounded maintenance batches without new inserts", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("schema-seed"), {
...database,
now: 1,
runtimeInstanceId: "runtime-1",
});
const { db } = openOpenClawStateDatabase(database);
db.exec("DELETE FROM execution_identity_contexts;");
db.prepare(
`WITH RECURSIVE rows(n) AS (
VALUES (1)
UNION ALL
SELECT n + 1 FROM rows WHERE n < 1025
)
INSERT INTO execution_identity_contexts (
context_id, execution_id, run_id, created_at, coverage_state, context_bytes, context_json
)
SELECT 'context-' || n, 'execution-' || n, 'run-' || n, 0, 'unattributed', 2, '{}'
FROM rows`,
).run();
expect(pruneExpiredExecutionIdentityContexts({ database, now: RETENTION_MS + 1 })).toBe(1_024);
expect(db.prepare("SELECT COUNT(*) AS count FROM execution_identity_contexts").get()).toEqual({
count: 1,
});
expect(pruneExpiredExecutionIdentityContexts({ database, now: RETENTION_MS + 1 })).toBe(1);
expect(db.prepare("SELECT COUNT(*) AS count FROM execution_identity_contexts").get()).toEqual({
count: 0,
});
});
it("prunes retention and row-cap overflow in bounded batches", () => {
const retentionDatabase = databaseOptions();
for (const runId of ["old-1", "old-2", "old-3"]) {
prepareExecutionIdentityContextAtAdmission(facts(runId), {
...retentionDatabase,
now: 0,
runtimeInstanceId: "runtime-1",
limits: { maxRows: 10, pruneBatchRows: 1 },
});
}
prepareExecutionIdentityContextAtAdmission(facts("new-1"), {
...retentionDatabase,
now: RETENTION_MS + 1,
runtimeInstanceId: "runtime-1",
limits: { maxRows: 1, pruneBatchRows: 1 },
});
const retainedAfterOneBatch = openOpenClawStateDatabase(retentionDatabase)
.db.prepare("SELECT COUNT(*) AS count FROM execution_identity_contexts")
.get() as { count: number };
expect(retainedAfterOneBatch.count).toBe(3);
const capDatabase = databaseOptions();
for (const runId of ["cap-1", "cap-2", "cap-3"]) {
prepareExecutionIdentityContextAtAdmission(facts(runId), {
...capDatabase,
now: 100,
contextId: `context-${runId}`,
runtimeInstanceId: "runtime-1",
limits: { maxRows: 2, pruneBatchRows: 1 },
});
}
const capped = openOpenClawStateDatabase(capDatabase)
.db.prepare("SELECT run_id FROM execution_identity_contexts ORDER BY context_id")
.all() as Array<{ run_id: string }>;
expect(capped).toHaveLength(2);
expect(capped.map((row) => row.run_id)).not.toContain("cap-1");
});
it("enforces the row cap across independent database connections", () => {
const database = databaseOptions();
const path = openOpenClawStateDatabase(database).path;
closeOpenClawStateDatabaseForTest();
const first = openIndependentStateDatabase(path);
const second = openIndependentStateDatabase(path);
try {
for (const [index, connection] of [first, second, first, second].entries()) {
const runId = `shared-cap-${index + 1}`;
prepareExecutionIdentityContextAtAdmission(facts(runId), {
database: connection,
now: 100 + index,
contextId: `context-${runId}`,
runtimeInstanceId: "runtime-1",
limits: { maxRows: 2, pruneBatchRows: 1 },
});
}
const retained = first.db
.prepare("SELECT run_id FROM execution_identity_contexts ORDER BY created_at")
.all() as Array<{ run_id: string }>;
expect(retained.map((row) => row.run_id)).toEqual(["shared-cap-3", "shared-cap-4"]);
} finally {
first.db.close();
second.db.close();
}
});
it("inspects through a read-only connection while another writer holds the database", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("held-lock-inspection"), {
...database,
now: 100,
contextId: "context-held-lock-inspection",
executionId: "execution-held-lock-inspection",
runtimeInstanceId: "runtime-1",
});
const path = openOpenClawStateDatabase(database).path;
closeOpenClawStateDatabaseForTest();
const lockDatabase = openNodeSqliteDatabase(path);
lockDatabase.exec("BEGIN IMMEDIATE");
try {
expect(
inspectExecutionIdentityRun(
{ executionId: "execution-held-lock-inspection" },
{ ...database, now: 100 },
),
).toMatchObject({
identity: {
state: "present",
context: {
contextId: "context-held-lock-inspection",
executionId: "execution-held-lock-inspection",
runId: "held-lock-inspection",
},
},
});
} finally {
lockDatabase.exec("ROLLBACK");
lockDatabase.close();
}
});
it("returns typed corrupt, unknown, and unsupported projections", () => {
const corruptDatabase = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-corrupt"), {
...corruptDatabase,
runtimeInstanceId: "runtime-1",
});
openOpenClawStateDatabase(corruptDatabase)
.db.prepare("UPDATE execution_identity_contexts SET context_json = ? WHERE run_id = ?")
.run("{", "run-corrupt");
expect(inspectExecutionIdentityRun({ runId: "run-corrupt" }, corruptDatabase)).toMatchObject({
run: { status: "known" },
identity: { state: "unknown", reasonCode: "identity_context_corrupt" },
coverage: { state: "unknown" },
});
const unknownDatabase = databaseOptions();
expect(inspectExecutionIdentityRun({ runId: "never-seen" }, unknownDatabase)).toMatchObject({
run: { status: "unknown" },
identity: {
state: "unknown",
reasonCode: "run_not_found",
remediation: [expect.objectContaining({ code: "verify_run_id" })],
},
});
recordAuditEvent(
{
sourceId: "legacy-run:1",
sourceSequence: 1,
occurredAt: Date.now(),
kind: "agent_run",
action: "agent.run.started",
status: "started",
actorType: "agent",
actorId: "main",
agentId: "main",
runId: "legacy-run",
},
unknownDatabase,
);
expect(inspectExecutionIdentityRun({ runId: "legacy-run" }, unknownDatabase)).toMatchObject({
run: { status: "known" },
identity: {
state: "unsupported",
reasonCode: "identity_context_unavailable",
remediation: [expect.objectContaining({ code: "record_new_identity_context" })],
},
});
});
it("projects one non-enforcement admission explanation", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-receipt"), {
...database,
now: 123,
contextId: "context-receipt",
runtimeInstanceId: "runtime-1",
});
const result = inspectExecutionIdentityRun({ runId: "run-receipt" }, { ...database, now: 123 });
expect(result.identity).toMatchObject({
state: "present",
context: { contextId: "context-receipt", coverageState: "unattributed" },
});
expect(result.decisions).toEqual([
expect.objectContaining({
decision: {
outcome: "not-applicable",
reasonCode: "run_admission_identity_not_evaluated",
},
enforcement: expect.objectContaining({
coverageState: "unattributed",
policyRefs: [],
grantRefs: [],
contextFieldsUsed: [],
}),
}),
]);
expect(
inspectExecutionIdentityRun(
{ runId: "run-receipt", decisionLimit: 1 },
{ ...database, now: 123 },
).nextDecisionCursor,
).toBeUndefined();
expect(
inspectExecutionIdentityRun(
{ runId: "run-receipt", decisionCursor: "a:0:0" },
{ ...database, now: 123 },
).decisions,
).toEqual([]);
});
it("projects an authoritative denied approval by run before and after restart", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-denied-receipt"), {
...database,
now: 100,
contextId: "context-denied-receipt",
executionId: "execution-denied-receipt",
runtimeInstanceId: "runtime-1",
});
recordDeniedApprovalForRun("run-denied-receipt", database, "denied-approval", {
contextId: "context-denied-receipt",
executionId: "execution-denied-receipt",
});
const beforeRestart = inspectExecutionIdentityRun(
{ runId: "run-denied-receipt" },
{ ...database, now: 300 },
);
expect(beforeRestart).toMatchObject({
coverage: { state: "enforced" },
decisions: [
{ decision: { outcome: "not-applicable" } },
{
contextId: "context-denied-receipt",
executionId: "execution-denied-receipt",
runId: "run-denied-receipt",
decision: {
outcome: "denied",
reasonCode: "operator_approval_denied_by_reviewer",
},
enforcement: {
coverageState: "enforced",
contextFieldsUsed: ["contextId", "executionId", "runId"],
},
source: { owner: "operator_approvals" },
},
],
});
expect(JSON.stringify(beforeRestart)).not.toContain("private-reviewer-device");
expect(JSON.stringify(beforeRestart)).not.toContain("private-tool-call");
closeOpenClawStateDatabaseForTest();
expect(
inspectExecutionIdentityRun({ runId: "run-denied-receipt" }, { ...database, now: 300 }),
).toEqual(beforeRestart);
expect(
inspectExecutionIdentityRun(
{ runId: "run-denied-receipt", decisionCursor: "a:0:0", decisionLimit: 1 },
{ ...database, now: 300 },
),
).toMatchObject({
decisions: [{ decision: { reasonCode: "operator_approval_denied_by_reviewer" } }],
});
expect(
inspectExecutionIdentityRun(
{ runId: "run-denied-receipt", decisionCursor: "1", decisionLimit: 1 },
{ ...database, now: 300 },
).decisions,
).toMatchObject([{ decision: { reasonCode: "operator_approval_denied_by_reviewer" } }]);
});
it("keeps a corrupt approval unknown before its decision page is returned", () => {
const database = databaseOptions();
prepareExecutionIdentityContextAtAdmission(facts("run-corrupt-approval"), {
...database,
now: 100,
contextId: "context-corrupt-approval",
executionId: "execution-corrupt-approval",
runtimeInstanceId: "runtime-1",
});
recordDeniedApprovalForRun("run-corrupt-approval", database, "corrupt-approval", {
contextId: "context-corrupt-approval",
executionId: "execution-corrupt-approval",
});
openOpenClawStateDatabase(database)
.db.prepare("UPDATE operator_approvals SET presentation_json = ? WHERE approval_id = ?")
.run("{", "corrupt-approval");
expect(
inspectExecutionIdentityRun(
{ executionId: "execution-corrupt-approval", decisionLimit: 1 },
{ ...database, now: 300 },
),
).toMatchObject({
coverage: {
state: "unknown",
missingEvidence: expect.arrayContaining(["operator_approval.valid"]),
},
decisions: [{ decision: { outcome: "not-applicable" } }],
nextDecisionCursor: "a:0:0",
});
});
it("reports a retained approval with no identity context as an unknown missing link", () => {
const database = databaseOptions();
recordDeniedApprovalForRun("run-missing-context", database);
expect(
inspectExecutionIdentityRun({ runId: "run-missing-context" }, { ...database, now: 300 }),
).toMatchObject({
run: { runId: "run-missing-context", status: "known" },
identity: {
state: "unknown",
reasonCode: "decision_context_link_missing",
},
decisions: [],
coverage: {
state: "unknown",
missingEvidence: ["identity.context", "decision.context_link"],
},
});
});
});