fix(feishu): land #31209 prevent system preview leakage (@stakeswky)

Landed from contributor PR #31209 by @stakeswky.

Co-authored-by: stakeswky <stakeswky@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-03-02 03:33:42 +00:00
parent 072e1e9e38
commit edd9319552
2 changed files with 37 additions and 2 deletions

View File

@@ -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.

View File

@@ -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 },