From 230266c0baad7d9cbfce2eb8a1951fae9e44e9aa Mon Sep 17 00:00:00 2001 From: Vincent Koc <25068+vincentkoc@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:35:28 +0800 Subject: [PATCH] fix(feishu): validate retry timestamp --- extensions/feishu/src/dedupe-key.test.ts | 7 +++++++ extensions/feishu/src/dedupe-key.ts | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/extensions/feishu/src/dedupe-key.test.ts b/extensions/feishu/src/dedupe-key.test.ts index f3603f0d8075..a41d1b98ac1f 100644 --- a/extensions/feishu/src/dedupe-key.test.ts +++ b/extensions/feishu/src/dedupe-key.test.ts @@ -64,6 +64,13 @@ describe("resolveFeishuMessageDedupeKey", () => { expect(key).toBe("om_no_time"); }); + it("falls back to message_id for malformed create_time", () => { + const key = resolveFeishuMessageDedupeKey( + textEvent({ messageId: "om_bad_time", createTime: "1710000000000ms" }), + ); + expect(key).toBe("om_bad_time"); + }); + it("keeps media keyed by message_id plus media key", () => { const event: FeishuMessageEvent = { sender: { sender_id: { open_id: "ou-user" } }, diff --git a/extensions/feishu/src/dedupe-key.ts b/extensions/feishu/src/dedupe-key.ts index 1c4ffe0688e7..4e657a9b653b 100644 --- a/extensions/feishu/src/dedupe-key.ts +++ b/extensions/feishu/src/dedupe-key.ts @@ -1,5 +1,6 @@ // Feishu plugin module implements dedupe key behavior. import { createHash } from "node:crypto"; +import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime"; import { asNullableRecord as readRecord } from "openclaw/plugin-sdk/string-coerce-runtime"; import type { FeishuMessageEvent } from "./event-types.js"; import { normalizeFeishuExternalKey } from "./external-keys.js"; @@ -79,7 +80,12 @@ function resolveTextRetryDedupeKey(event: FeishuMessageDedupeInput): string | un const createTime = event.message.create_time?.trim(); const chatId = event.message.chat_id?.trim(); const senderId = resolveSenderIdentity(event); - if (!createTime || !chatId || !senderId) { + if ( + !createTime || + parseStrictNonNegativeInteger(createTime) === undefined || + !chatId || + !senderId + ) { return undefined; } const contentHash = createHash("sha256")