diff --git a/CHANGELOG.md b/CHANGELOG.md index cbf52b6bb3f..68ad5f8b1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Approvals/security: require explicit chat exec-approval enablement instead of auto-enabling approval clients just because approvers resolve from config or owner allowlists. (#70715) Thanks @vincentkoc. - Discord/security: keep native slash-command channel policy from bypassing configured owner or member restrictions, while preserving channel-policy fallback when no stricter access rule exists. (#70711) Thanks @vincentkoc. - Android/security: stop `ASK_OPENCLAW` intents from auto-sending injected prompts, so external app actions only prefill the draft instead of dispatching it immediately. (#70714) Thanks @vincentkoc. - Control UI/chat: queue Stop-button aborts across Gateway reconnects so a disconnected active run is canceled on reconnect instead of only clearing local UI state. (#70673) Thanks @chinar-amrutkar. diff --git a/extensions/discord/src/exec-approvals.test.ts b/extensions/discord/src/exec-approvals.test.ts index 2c5a169c4ce..497a24ca1ec 100644 --- a/extensions/discord/src/exec-approvals.test.ts +++ b/extensions/discord/src/exec-approvals.test.ts @@ -22,7 +22,7 @@ function buildConfig( } describe("discord exec approvals", () => { - it("auto-enables when owner approvers resolve and disables only when forced off", () => { + it("requires explicit enablement even when owner approvers resolve", () => { expect(isDiscordExecApprovalClientEnabled({ cfg: buildConfig() })).toBe(false); expect( isDiscordExecApprovalClientEnabled({ @@ -33,7 +33,7 @@ describe("discord exec approvals", () => { isDiscordExecApprovalClientEnabled({ cfg: buildConfig({ approvers: ["123"] }), }), - ).toBe(true); + ).toBe(false); expect( isDiscordExecApprovalClientEnabled({ cfg: { @@ -41,6 +41,11 @@ describe("discord exec approvals", () => { commands: { ownerAllowFrom: ["discord:789"] }, } as OpenClawConfig, }), + ).toBe(false); + expect( + isDiscordExecApprovalClientEnabled({ + cfg: buildConfig({ enabled: "auto", approvers: ["123"] }), + }), ).toBe(true); expect( isDiscordExecApprovalClientEnabled({ diff --git a/extensions/slack/src/exec-approvals.test.ts b/extensions/slack/src/exec-approvals.test.ts index 4829afc5aae..faeebb0f3ae 100644 --- a/extensions/slack/src/exec-approvals.test.ts +++ b/extensions/slack/src/exec-approvals.test.ts @@ -29,7 +29,7 @@ function buildConfig( } describe("slack exec approvals", () => { - it("auto-enables when owner approvers resolve and disables only when forced off", () => { + it("requires explicit enablement even when owner approvers resolve", () => { expect(isSlackExecApprovalClientEnabled({ cfg: buildConfig() })).toBe(false); expect( isSlackExecApprovalClientEnabled({ @@ -40,7 +40,7 @@ describe("slack exec approvals", () => { isSlackExecApprovalClientEnabled({ cfg: buildConfig({ approvers: ["U123"] }), }), - ).toBe(true); + ).toBe(false); expect( isSlackExecApprovalClientEnabled({ cfg: { @@ -48,6 +48,11 @@ describe("slack exec approvals", () => { commands: { ownerAllowFrom: ["slack:U123OWNER"] }, } as OpenClawConfig, }), + ).toBe(false); + expect( + isSlackExecApprovalClientEnabled({ + cfg: buildConfig({ enabled: "auto", approvers: ["U123"] }), + }), ).toBe(true); expect( isSlackExecApprovalClientEnabled({ diff --git a/extensions/telegram/src/exec-approvals.test.ts b/extensions/telegram/src/exec-approvals.test.ts index 2b318f4f467..e6a748faa90 100644 --- a/extensions/telegram/src/exec-approvals.test.ts +++ b/extensions/telegram/src/exec-approvals.test.ts @@ -110,7 +110,7 @@ function makeForeignChannelApprovalRequest(params: { } describe("telegram exec approvals", () => { - it("auto-enables when approvers resolve and disables only when forced off", () => { + it("requires explicit enablement even when approvers resolve", () => { expect(isTelegramExecApprovalClientEnabled({ cfg: buildConfig() })).toBe(false); expect( isTelegramExecApprovalClientEnabled({ @@ -121,11 +121,16 @@ describe("telegram exec approvals", () => { isTelegramExecApprovalClientEnabled({ cfg: buildConfig(undefined, { allowFrom: ["123"] }), }), - ).toBe(true); + ).toBe(false); expect( isTelegramExecApprovalClientEnabled({ cfg: buildConfig({ approvers: ["123"] }), }), + ).toBe(false); + expect( + isTelegramExecApprovalClientEnabled({ + cfg: buildConfig({ enabled: "auto", approvers: ["123"] }), + }), ).toBe(true); expect( isTelegramExecApprovalClientEnabled({ diff --git a/src/plugin-sdk/approval-client-helpers.test.ts b/src/plugin-sdk/approval-client-helpers.test.ts index 926aadf661a..3a3b2585cbf 100644 --- a/src/plugin-sdk/approval-client-helpers.test.ts +++ b/src/plugin-sdk/approval-client-helpers.test.ts @@ -77,12 +77,12 @@ describe("createChannelExecApprovalProfile", () => { matchesRequestAccount: ({ accountId }) => accountId !== "other", }); - it("treats unset enabled as auto and false as disabled", () => { + it("requires explicit enablement when approvers exist", () => { expect( isChannelExecApprovalClientEnabledFromConfig({ approverCount: 1, }), - ).toBe(true); + ).toBe(false); expect( isChannelExecApprovalClientEnabledFromConfig({ enabled: "auto", diff --git a/src/plugin-sdk/approval-client-helpers.ts b/src/plugin-sdk/approval-client-helpers.ts index 3c73703ef7f..e4f6b956b7b 100644 --- a/src/plugin-sdk/approval-client-helpers.ts +++ b/src/plugin-sdk/approval-client-helpers.ts @@ -44,7 +44,7 @@ export function isChannelExecApprovalClientEnabledFromConfig(params: { if (params.approverCount <= 0) { return false; } - return params.enabled !== false; + return params.enabled === true || params.enabled === "auto"; } export function isChannelExecApprovalTargetRecipient(params: {