fix(feishu): bound approval card expiry

This commit is contained in:
Peter Steinberger
2026-05-30 11:41:43 -04:00
parent 05634708e0
commit 19f22b5924
2 changed files with 58 additions and 1 deletions

View File

@@ -270,6 +270,45 @@ describe("Feishu Card Action Handler", () => {
expect(handleFeishuMessage).not.toHaveBeenCalled();
});
it("does not open approval cards when the expiry would exceed a valid Date", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date(8_640_000_000_000_000));
try {
const event: FeishuCardActionEvent = {
operator: { open_id: "u123", user_id: "uid1", union_id: "un1" },
token: "tok4-boundary",
action: {
value: createFeishuCardInteractionEnvelope({
k: "meta",
a: FEISHU_APPROVAL_REQUEST_ACTION,
m: {
command: "/new",
prompt: "Start a fresh session?",
},
c: {
u: "u123",
h: "chat1",
t: "group",
s: "agent:codex:feishu:chat:chat1",
e: 8_640_000_000_000_000,
},
}),
tag: "button",
},
context: { open_id: "u123", user_id: "uid1", chat_id: "chat1" },
};
await handleFeishuCardAction({ cfg, event, runtime, accountId: "main" });
expect(sendCardFeishuMock).not.toHaveBeenCalled();
const sendMessage = sendMessageCall();
expect(sendMessage.to).toBe("chat:chat1");
expect(String(sendMessage.text)).toContain("payload is invalid");
} finally {
vi.useRealTimers();
}
});
it("runs approval confirmation through the normal message path", async () => {
const event = createStructuredQuickActionEvent({
token: "tok5",

View File

@@ -217,6 +217,13 @@ function sanitizeLogValue(v: string): string {
return v.replace(/[\r\n]/g, " ").slice(0, 500);
}
function resolveFeishuApprovalCardExpiresAt(nowRaw = Date.now()): number | undefined {
const now = asDateTimestampMs(nowRaw);
return now === undefined
? undefined
: resolveExpiresAtMsFromDurationMs(FEISHU_APPROVAL_CARD_TTL_MS, { nowMs: now });
}
function cacheResolvedCardActionChatType(
cacheKey: string,
value: "p2p" | "group",
@@ -373,6 +380,17 @@ export async function handleFeishuCardAction(params: {
typeof envelope.m?.prompt === "string" && envelope.m.prompt.trim()
? envelope.m.prompt
: `Run \`${command}\` in this Feishu conversation?`;
const expiresAt = resolveFeishuApprovalCardExpiresAt();
if (expiresAt === undefined) {
await sendInvalidInteractionNotice({
cfg,
event,
reason: "malformed",
accountId,
});
completeFeishuCardActionToken({ token: event.token, accountId: account.accountId });
return;
}
await sendCardFeishu({
cfg,
to: resolveCallbackTarget(event),
@@ -382,7 +400,7 @@ export async function handleFeishuCardAction(params: {
command,
prompt,
sessionKey: envelope.c?.s,
expiresAt: Date.now() + FEISHU_APPROVAL_CARD_TTL_MS,
expiresAt,
chatType: await resolveCardActionChatType({
event,
account,