Skip to content

Commit edd9319

Browse files
steipetestakeswky
andcommitted
fix(feishu): land #31209 prevent system preview leakage (@stakeswky)
Landed from contributor PR #31209 by @stakeswky. Co-authored-by: stakeswky <[email protected]>
1 parent 072e1e9 commit edd9319

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ Docs: https://docs.openclaw.ai
113113
### Fixes
114114

115115
- 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.
116-
- 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.
116+
- 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.
117+
- 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.
117118
- 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.
118119
- 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.
119120
- 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.

extensions/feishu/src/bot.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,15 @@ describe("handleFeishuMessage command authorization", () => {
120120
const mockReadAllowFromStore = vi.fn().mockResolvedValue([]);
121121
const mockUpsertPairingRequest = vi.fn().mockResolvedValue({ code: "ABCDEFGH", created: false });
122122
const mockBuildPairingReply = vi.fn(() => "Pairing response");
123+
const mockEnqueueSystemEvent = vi.fn();
123124
const mockSaveMediaBuffer = vi.fn().mockResolvedValue({
124125
path: "/tmp/inbound-clip.mp4",
125126
contentType: "video/mp4",
126127
});
127128

128129
beforeEach(() => {
129130
vi.clearAllMocks();
131+
mockShouldComputeCommandAuthorized.mockReset().mockReturnValue(true);
130132
mockResolveAgentRoute.mockReturnValue({
131133
agentId: "main",
132134
accountId: "default",
@@ -140,9 +142,10 @@ describe("handleFeishuMessage command authorization", () => {
140142
},
141143
},
142144
});
145+
mockEnqueueSystemEvent.mockReset();
143146
setFeishuRuntime({
144147
system: {
145-
enqueueSystemEvent: vi.fn(),
148+
enqueueSystemEvent: mockEnqueueSystemEvent,
146149
},
147150
channel: {
148151
routing: {
@@ -174,6 +177,37 @@ describe("handleFeishuMessage command authorization", () => {
174177
} as unknown as PluginRuntime);
175178
});
176179

180+
it("does not enqueue inbound preview text as system events", async () => {
181+
mockShouldComputeCommandAuthorized.mockReturnValue(false);
182+
183+
const cfg: ClawdbotConfig = {
184+
channels: {
185+
feishu: {
186+
dmPolicy: "open",
187+
},
188+
},
189+
} as ClawdbotConfig;
190+
191+
const event: FeishuMessageEvent = {
192+
sender: {
193+
sender_id: {
194+
open_id: "ou-attacker",
195+
},
196+
},
197+
message: {
198+
message_id: "msg-no-system-preview",
199+
chat_id: "oc-dm",
200+
chat_type: "p2p",
201+
message_type: "text",
202+
content: JSON.stringify({ text: "hi there" }),
203+
},
204+
};
205+
206+
await dispatchMessage({ cfg, event });
207+
208+
expect(mockEnqueueSystemEvent).not.toHaveBeenCalled();
209+
});
210+
177211
it("uses authorizer resolution instead of hardcoded CommandAuthorized=true", async () => {
178212
const cfg: ClawdbotConfig = {
179213
commands: { useAccessGroups: true },

0 commit comments

Comments
 (0)