mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
test(security): cover detached approval persistence failures
This commit is contained in:
parent
af6d851858
commit
7aaa91a1d7
1 changed files with 47 additions and 12 deletions
|
|
@ -1480,22 +1480,57 @@ EOF`,
|
||||||
});
|
});
|
||||||
recordAllowlistMatchesUseMock.mockRejectedValueOnce(new Error("approval lock unavailable"));
|
recordAllowlistMatchesUseMock.mockRejectedValueOnce(new Error("approval lock unavailable"));
|
||||||
buildExecApprovalFollowupTargetMock.mockImplementation((value) => value);
|
buildExecApprovalFollowupTargetMock.mockImplementation((value) => value);
|
||||||
|
const captured = captureSecurityEvents();
|
||||||
|
|
||||||
const result = await runGatewayAllowlist({ command: "echo approved" });
|
let result: Awaited<ReturnType<typeof runGatewayAllowlist>>;
|
||||||
|
try {
|
||||||
|
result = await runGatewayAllowlist({ command: "echo approved" });
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(sendExecApprovalFollowupResultMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
captured.stop();
|
||||||
|
}
|
||||||
|
|
||||||
expect(result.pendingResult?.details.status).toBe("approval-pending");
|
expect(result!.pendingResult?.details.status).toBe("approval-pending");
|
||||||
await vi.waitFor(() => {
|
|
||||||
expect(sendExecApprovalFollowupResultMock).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
expect(requireSentFollowupText(0)).toContain("approval-state-write-failed");
|
expect(requireSentFollowupText(0)).toContain("approval-state-write-failed");
|
||||||
expect(runExecProcessMock).not.toHaveBeenCalled();
|
expect(runExecProcessMock).not.toHaveBeenCalled();
|
||||||
expect(emitTrustedSecurityEventMock).toHaveBeenCalledWith(
|
expect(captured.events.at(-1)).toMatchObject({
|
||||||
expect.objectContaining({
|
action: "exec.approval.denied",
|
||||||
action: "exec.approval.denied",
|
outcome: "error",
|
||||||
outcome: "error",
|
policy: { reason: "approval-state-write-failed" },
|
||||||
policy: expect.objectContaining({ reason: "approval-state-write-failed" }),
|
});
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
it("fails closed when a detached allow-always grant cannot be persisted", async () => {
|
||||||
|
resolveApprovalDecisionOrUndefinedMock.mockResolvedValue("allow-always");
|
||||||
|
createExecApprovalDecisionStateMock.mockReturnValue({
|
||||||
|
baseDecision: { timedOut: false },
|
||||||
|
approvedByAsk: true,
|
||||||
|
deniedReason: null,
|
||||||
|
});
|
||||||
|
persistAllowAlwaysDecisionMock.mockRejectedValueOnce(new Error("approval lock unavailable"));
|
||||||
|
buildExecApprovalFollowupTargetMock.mockImplementation((value) => value);
|
||||||
|
const captured = captureSecurityEvents();
|
||||||
|
|
||||||
|
let result: Awaited<ReturnType<typeof runGatewayAllowlist>>;
|
||||||
|
try {
|
||||||
|
result = await runGatewayAllowlist({ command: "echo approved" });
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(sendExecApprovalFollowupResultMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
captured.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result!.pendingResult?.details.status).toBe("approval-pending");
|
||||||
|
expect(requireSentFollowupText(0)).toContain("approval-state-write-failed");
|
||||||
|
expect(runExecProcessMock).not.toHaveBeenCalled();
|
||||||
|
expect(captured.events.at(-1)).toMatchObject({
|
||||||
|
action: "exec.approval.denied",
|
||||||
|
outcome: "error",
|
||||||
|
policy: { reason: "approval-state-write-failed" },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("waits inline for webchat approval so the exec tool can return real output to the model", async () => {
|
it("waits inline for webchat approval so the exec tool can return real output to the model", async () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue