From fcbc254d0d02ac3108df7d64406c01d7edcca955 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 28 May 2026 15:55:14 -0400 Subject: [PATCH] fix: validate feishu action count params --- extensions/feishu/src/channel.test.ts | 43 +++++++++++++++++++++++++++ extensions/feishu/src/channel.ts | 24 ++++++++++----- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/extensions/feishu/src/channel.test.ts b/extensions/feishu/src/channel.test.ts index 1c55a413159..bfba070b606 100644 --- a/extensions/feishu/src/channel.test.ts +++ b/extensions/feishu/src/channel.test.ts @@ -1041,6 +1041,49 @@ describe("feishuPlugin actions", () => { expect(requireRecord(peers[0], "peer").id).toBe("ou_1"); }); + it("ignores malformed channel-list limits", async () => { + listFeishuDirectoryGroupsLiveMock.mockResolvedValueOnce([{ kind: "group", id: "oc_group_1" }]); + + await feishuPlugin.actions?.handleAction?.({ + action: "channel-list", + params: { query: "eng", limit: "-1", scope: "groups" }, + cfg, + accountId: undefined, + } as never); + + expect(listFeishuDirectoryGroupsLiveMock).toHaveBeenCalledWith({ + cfg, + query: "eng", + limit: undefined, + fallbackToStatic: false, + accountId: undefined, + }); + }); + + it("ignores non-decimal Feishu action page sizes", async () => { + getChatMembersMock.mockResolvedValueOnce({ + chat_id: "oc_group_1", + members: [], + has_more: false, + }); + + await feishuPlugin.actions?.handleAction?.({ + action: "member-info", + params: { chatId: "oc_group_1", pageSize: "0x10" }, + cfg, + accountId: undefined, + toolContext: {}, + } as never); + + expect(getChatMembersMock).toHaveBeenCalledWith( + { tag: "client" }, + "oc_group_1", + undefined, + undefined, + "open_id", + ); + }); + it("fails channel-list when live discovery fails", async () => { listFeishuDirectoryGroupsLiveMock.mockRejectedValueOnce(new Error("token expired")); diff --git a/extensions/feishu/src/channel.ts b/extensions/feishu/src/channel.ts index 6cbc92b2629..55f37d1361d 100644 --- a/extensions/feishu/src/channel.ts +++ b/extensions/feishu/src/channel.ts @@ -556,15 +556,23 @@ function readFirstString( return undefined; } -function readOptionalNumber(params: Record, keys: string[]): number | undefined { +function isPositiveSafeInteger(value: number): boolean { + return Number.isSafeInteger(value) && value > 0; +} + +function readOptionalPositiveInteger( + params: Record, + keys: string[], +): number | undefined { for (const key of keys) { const value = params[key]; - if (typeof value === "number" && Number.isFinite(value)) { + if (typeof value === "number" && isPositiveSafeInteger(value)) { return value; } if (typeof value === "string" && value.trim()) { - const parsed = Number(value); - if (Number.isFinite(parsed)) { + const trimmed = value.trim(); + const parsed = /^\d+$/.test(trimmed) ? Number(trimmed) : Number.NaN; + if (isPositiveSafeInteger(parsed)) { return parsed; } } @@ -939,7 +947,7 @@ export const feishuPlugin: ChannelPlugin