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.<chat_id>.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.
This commit is contained in:
MoerAI
2026-04-27 20:59:26 +09:00
committed by Peter Steinberger
parent 346d5c28c1
commit 01e153986a
2 changed files with 58 additions and 6 deletions

View File

@@ -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.<chat_id>.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: {

View File

@@ -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.<chat_id>`
// is treated as admitted regardless of `groupAllowFrom`. The reporter case in
// #67687 only sets `groups.<chat_id>.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(