From 01e153986af555a9df2baad40386b6c1ba2d45f6 Mon Sep 17 00:00:00 2001 From: MoerAI Date: Mon, 27 Apr 2026 20:59:26 +0900 Subject: [PATCH] fix(feishu): admit groups explicitly listed under channels.feishu.groups (#67687) Feishu config defaults groupPolicy to 'allowlist'. Inbound group handling read groupAllowFrom and called isFeishuGroupAllowed before resolveFeishuReplyPolicy was reached, so a config that only set channels.feishu.groups..requireMention=false (with no groupAllowFrom) was rejected with 'group not in groupAllowFrom' before per-group requireMention could take effect. Treat the explicit presence of a group entry under channels.feishu.groups as the operator's allowlist signal: if groupConfig is defined, skip the empty-allowlist rejection. resolveFeishuReplyPolicy still owns mention gating, and existing groupConfig.enabled=false / groupAllowFrom-driven rejections are preserved. Adds a regression test that exercises the reporter's exact config shape and confirms inbound text reaches finalize/dispatch. --- extensions/feishu/src/bot.test.ts | 42 +++++++++++++++++++++++++++++++ extensions/feishu/src/bot.ts | 22 +++++++++++----- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index 87889d5e50c..24d50303cd5 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -1414,6 +1414,48 @@ describe("handleFeishuMessage command authorization", () => { expect(mockDispatchReplyFromConfig).not.toHaveBeenCalled(); }); + it("admits group when chat_id is explicitly configured under groups, even with empty groupAllowFrom (#67687)", async () => { + // Regression for #67687: a group that only sets `groups..requireMention=false` + // (and leaves `groupAllowFrom` empty) should still be admitted under the schema-default + // `groupPolicy="allowlist"`. The group's explicit presence in `channels.feishu.groups` + // is the operator's allowlist signal, and the per-group `requireMention` override should + // then control mention gating for inbound text events. + mockShouldComputeCommandAuthorized.mockReturnValue(false); + + const cfg: ClawdbotConfig = { + channels: { + feishu: { + // groupPolicy intentionally omitted -> schema default is "allowlist" + // groupAllowFrom intentionally omitted -> empty [] + groups: { + "oc-explicit-group": { + requireMention: false, + }, + }, + }, + }, + } as ClawdbotConfig; + + const event: FeishuMessageEvent = { + sender: { + sender_id: { open_id: "ou-sender" }, + }, + message: { + message_id: "msg-explicit-group-67687", + chat_id: "oc-explicit-group", + chat_type: "group", + message_type: "text", + content: JSON.stringify({ text: "hello bot" }), + }, + }; + + await dispatchMessage({ cfg, event }); + + // Group must be admitted: the inbound finalize/dispatch path runs. + expect(mockFinalizeInboundContext).toHaveBeenCalled(); + expect(mockDispatchReplyFromConfig).toHaveBeenCalled(); + }); + it("drops message when groupConfig.enabled is false", async () => { const cfg: ClawdbotConfig = { channels: { diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index 154e81bc06d..aefbf41c780 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -554,13 +554,23 @@ export async function handleFeishuMessage(params: { const groupAllowFrom = feishuCfg?.groupAllowFrom ?? []; // DEBUG: log(`feishu[${account.accountId}]: groupPolicy=${groupPolicy}`); + // A group that is explicitly configured under `channels.feishu.groups.` + // is treated as admitted regardless of `groupAllowFrom`. The reporter case in + // #67687 only sets `groups..requireMention=false` and leaves + // `groupAllowFrom` empty; with the schema-default `groupPolicy="allowlist"`, + // an empty allowlist would otherwise reject the group before any per-group + // `requireMention` override is evaluated. + const groupExplicitlyConfigured = groupConfig !== undefined; + // Check if this GROUP is allowed (groupAllowFrom contains group IDs like oc_xxx, not user IDs) - const groupAllowed = isFeishuGroupAllowed({ - groupPolicy, - allowFrom: groupAllowFrom, - senderId: ctx.chatId, // Check group ID, not sender ID - senderName: undefined, - }); + const groupAllowed = + groupExplicitlyConfigured || + isFeishuGroupAllowed({ + groupPolicy, + allowFrom: groupAllowFrom, + senderId: ctx.chatId, // Check group ID, not sender ID + senderName: undefined, + }); if (!groupAllowed) { log(