From ec391edea78d77a48eae9219fde19081d12839bf Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Mon, 27 Jul 2026 16:07:11 +0800 Subject: [PATCH] fix: clarify unstored exec approval defaults (#114417) --- src/cli/exec-approvals-cli.test.ts | 16 ++++++++++++++-- src/cli/exec-approvals-cli.ts | 11 +++++++---- src/cli/exec-policy-cli.test.ts | 20 ++++++++++++++++++++ src/cli/exec-policy-cli.ts | 8 +++++--- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/cli/exec-approvals-cli.test.ts b/src/cli/exec-approvals-cli.test.ts index f9af49aa9ea0..a6783a40c44e 100644 --- a/src/cli/exec-approvals-cli.test.ts +++ b/src/cli/exec-approvals-cli.test.ts @@ -169,6 +169,7 @@ function scopeByLabel(label: string, output: Record = writtenJs } function resetLocalSnapshot() { + localSnapshot.exists = true; localSnapshot.hash = "hash-local"; localSnapshot.file = { version: 1, agents: {} }; } @@ -309,6 +310,17 @@ describe("exec approvals CLI", () => { expect(runtimeErrors).toHaveLength(0); }); + it("renders an unstored fresh-install policy as defaults instead of absent", async () => { + localSnapshot.exists = false; + + await runApprovalsCommand(["approvals", "get"]); + + const output = defaultRuntime.log.mock.calls.map(([line]) => String(line ?? "")).join("\n"); + expect(output).toContain("State"); + expect(output).toContain("defaults (no stored overrides)"); + expect(output).not.toContain("Exists"); + }); + it("adds effective policy to json output", async () => { localSnapshot.file = { version: 1, @@ -329,7 +341,7 @@ describe("exec approvals CLI", () => { expect(defaultRuntime.writeJson).toHaveBeenCalledWith(writtenJson(), 0); const policy = effectivePolicy(); expect(String(policy.note)).toContain( - "Effective exec policy is the host approvals file intersected with requested tools.exec policy.", + "Effective exec policy is the host approvals policy intersected with requested tools.exec policy.", ); expect(String(policy.note)).toContain(SESSION_EXEC_OVERRIDES_NOTE); const scope = scopeByLabel("tools.exec"); @@ -430,7 +442,7 @@ describe("exec approvals CLI", () => { expect(defaultRuntime.writeJson).toHaveBeenCalledWith(writtenJson(), 0); const policy = effectivePolicy(); expect(String(policy.note)).toContain( - "Effective exec policy is the node host approvals file intersected with gateway tools.exec policy.", + "Effective exec policy is the node host approvals policy intersected with gateway tools.exec policy.", ); expect(String(policy.note)).toContain(SESSION_EXEC_OVERRIDES_NOTE); const scope = scopeByLabel("tools.exec"); diff --git a/src/cli/exec-approvals-cli.ts b/src/cli/exec-approvals-cli.ts index e4a61710b6cc..8a110ed0cc2f 100644 --- a/src/cli/exec-approvals-cli.ts +++ b/src/cli/exec-approvals-cli.ts @@ -757,7 +757,7 @@ function buildEffectivePolicyReport(params: { scopes: [], note: params.nativePolicy ? "This node enforces a host-native exec policy; OpenClaw approvals-file policy math does not apply." - : "Approvals file unavailable.", + : "Host approvals policy unavailable.", }; } if (params.source === "node") { @@ -784,7 +784,7 @@ function buildEffectivePolicyReport(params: { hostDefaultSource: "node-reported resolved defaults", }), note: - "Effective exec policy is the node host approvals file intersected with gateway tools.exec policy. " + + "Effective exec policy is the node host approvals policy intersected with gateway tools.exec policy. " + SESSION_EXEC_OVERRIDES_NOTE, }; } @@ -801,7 +801,7 @@ function buildEffectivePolicyReport(params: { hostPath: params.hostPath, }), note: - "Effective exec policy is the host approvals file intersected with requested tools.exec policy. " + + "Effective exec policy is the host approvals policy intersected with requested tools.exec policy. " + SESSION_EXEC_OVERRIDES_NOTE, }; } @@ -887,7 +887,10 @@ function renderApprovalsSnapshot(snapshot: ExecApprovalsSnapshot, targetLabel: s const summaryRows = [ { Field: "Target", Value: targetLabel }, { Field: "Path", Value: snapshot.path }, - { Field: "Exists", Value: snapshot.exists ? "yes" : "no" }, + { + Field: "State", + Value: snapshot.exists ? "stored" : "defaults (no stored overrides)", + }, { Field: "Hash", Value: snapshot.hash }, { Field: "Version", Value: String(file.version ?? 1) }, { Field: "Socket", Value: file.socket?.path ?? "default" }, diff --git a/src/cli/exec-policy-cli.test.ts b/src/cli/exec-policy-cli.test.ts index 0e867c03db6c..ed61acdc3cc5 100644 --- a/src/cli/exec-policy-cli.test.ts +++ b/src/cli/exec-policy-cli.test.ts @@ -326,6 +326,26 @@ describe("exec-policy CLI", () => { }); }); + it("renders an unstored fresh-install policy as defaults instead of missing", async () => { + mocks.readExecApprovalsSnapshot.mockImplementationOnce(() => ({ + path: "~/.openclaw/state/openclaw.sqlite#exec_approvals_config", + exists: false, + raw: null, + hash: "missing-hash", + file: { version: 1, agents: {} }, + })); + + await runExecPolicyCommand(["exec-policy", "show"]); + + const output = stripAnsi( + mocks.defaultRuntime.log.mock.calls.map((call) => String(call[0] ?? "")).join("\n"), + ); + expect(output).toContain("Approvals State"); + expect(output).toContain("defaults (no stored overrides)"); + expect(output).not.toContain("Approvals File"); + expect(output).not.toContain("missing"); + }); + it("marks host=node scopes as node-managed in show output", async () => { mocks.setConfig({ tools: { diff --git a/src/cli/exec-policy-cli.ts b/src/cli/exec-policy-cli.ts index 5af5e943a25d..56da8316cce7 100644 --- a/src/cli/exec-policy-cli.ts +++ b/src/cli/exec-policy-cli.ts @@ -287,7 +287,7 @@ async function buildLocalExecPolicyShowPayload(): Promise ); const baseNote = hasNodeRuntimeScope ? "Scopes requesting host=node are node-managed at runtime. Local approvals are shown only for local/gateway scopes." - : "Effective exec policy is the host approvals file intersected with requested tools.exec policy."; + : "Effective exec policy is the host approvals policy intersected with requested tools.exec policy."; return { configPath: configSnapshot.path, approvalsPath: approvalsSnapshot.path, @@ -349,8 +349,10 @@ function renderExecPolicyShow(payload: ExecPolicyShowPayload): void { { Field: "Config", Value: sanitizeExecPolicyTableCell(payload.configPath) }, { Field: "Approvals", Value: sanitizeExecPolicyTableCell(payload.approvalsPath) }, { - Field: "Approvals File", - Value: sanitizeExecPolicyTableCell(payload.approvalsExists ? "present" : "missing"), + Field: "Approvals State", + Value: sanitizeExecPolicyTableCell( + payload.approvalsExists ? "stored" : "defaults (no stored overrides)", + ), }, ], }).trimEnd(),