diff --git a/CHANGELOG.md b/CHANGELOG.md index 35de9cada51..58e9d291cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,7 +113,8 @@ Docs: https://docs.openclaw.ai ### Fixes - Signal/Sync message null-handling: treat `syncMessage` presence (including `null`) as sync envelope traffic so replayed sentTranscript payloads cannot bypass loop guards after daemon restart. Landed from contributor PR #31138 by @Sid-Qin. Thanks @Sid-Qin. -- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, prevent inbound preview text from leaking into prompt system events, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #31209, #29610, #30432, #30331, and #29501. Thanks @stakeswky, @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff. +- Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky. +- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff. - Google Chat/Thread replies: set `messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD` on threaded sends so replies attach to existing threads instead of silently failing thread placement. Landed from contributor PR #30965 by @novan. Thanks @novan. - Mattermost/Private channel policy routing: map Mattermost private channel type `P` to group chat type so `groupPolicy`/`groupAllowFrom` gates apply correctly instead of being treated as open public channels. Landed from contributor PR #30891 by @BlueBirdBack. Thanks @BlueBirdBack. - Models/Custom provider keys: trim custom provider map keys during normalization so image-capable models remain discoverable when provider keys are configured with leading/trailing whitespace. Landed from contributor PR #31202 by @stakeswky. Thanks @stakeswky. diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index c7be3c59561..529a49b0f8e 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -120,6 +120,7 @@ describe("handleFeishuMessage command authorization", () => { const mockReadAllowFromStore = vi.fn().mockResolvedValue([]); const mockUpsertPairingRequest = vi.fn().mockResolvedValue({ code: "ABCDEFGH", created: false }); const mockBuildPairingReply = vi.fn(() => "Pairing response"); + const mockEnqueueSystemEvent = vi.fn(); const mockSaveMediaBuffer = vi.fn().mockResolvedValue({ path: "/tmp/inbound-clip.mp4", contentType: "video/mp4", @@ -127,6 +128,7 @@ describe("handleFeishuMessage command authorization", () => { beforeEach(() => { vi.clearAllMocks(); + mockShouldComputeCommandAuthorized.mockReset().mockReturnValue(true); mockResolveAgentRoute.mockReturnValue({ agentId: "main", accountId: "default", @@ -140,9 +142,10 @@ describe("handleFeishuMessage command authorization", () => { }, }, }); + mockEnqueueSystemEvent.mockReset(); setFeishuRuntime({ system: { - enqueueSystemEvent: vi.fn(), + enqueueSystemEvent: mockEnqueueSystemEvent, }, channel: { routing: { @@ -174,6 +177,37 @@ describe("handleFeishuMessage command authorization", () => { } as unknown as PluginRuntime); }); + it("does not enqueue inbound preview text as system events", async () => { + mockShouldComputeCommandAuthorized.mockReturnValue(false); + + const cfg: ClawdbotConfig = { + channels: { + feishu: { + dmPolicy: "open", + }, + }, + } as ClawdbotConfig; + + const event: FeishuMessageEvent = { + sender: { + sender_id: { + open_id: "ou-attacker", + }, + }, + message: { + message_id: "msg-no-system-preview", + chat_id: "oc-dm", + chat_type: "p2p", + message_type: "text", + content: JSON.stringify({ text: "hi there" }), + }, + }; + + await dispatchMessage({ cfg, event }); + + expect(mockEnqueueSystemEvent).not.toHaveBeenCalled(); + }); + it("uses authorizer resolution instead of hardcoded CommandAuthorized=true", async () => { const cfg: ClawdbotConfig = { commands: { useAccessGroups: true },