Medium severity5.3NVD Advisory· Published Apr 10, 2026· Updated Apr 13, 2026
CVE-2026-35664
CVE-2026-35664
Description
OpenClaw before 2026.3.25 contains an authentication bypass vulnerability in raw card send surface that allows unpaired recipients to mint legacy callback payloads. Attackers can send raw card commands to bypass DM pairing restrictions and reach callback handling without proper authorization.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.3.28 | 2026.3.28 |
Affected products
1Patches
181c45976db53Feishu: reject legacy raw card command payloads (#55130)
2 files changed · +182 −0
extensions/feishu/src/channel.test.ts+148 −0 modified@@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../runtime-api.js"; +import { createFeishuCardInteractionEnvelope } from "./card-interaction.js"; import { looksLikeFeishuId, normalizeFeishuTarget, resolveReceiveIdType } from "./targets.js"; const probeFeishuMock = vi.hoisted(() => vi.fn()); @@ -206,6 +207,153 @@ describe("feishuPlugin actions", () => { expect(result?.details).toMatchObject({ ok: true, messageId: "om_card", chatId: "oc_group_1" }); }); + it("allows structured card button payloads", async () => { + sendCardFeishuMock.mockResolvedValueOnce({ messageId: "om_card", chatId: "oc_group_1" }); + const card = { + schema: "2.0", + body: { + elements: [ + { + tag: "action", + actions: [ + { + tag: "button", + text: { tag: "plain_text", content: "Run /new" }, + value: createFeishuCardInteractionEnvelope({ + k: "quick", + a: "feishu.quick_actions.help", + q: "/help", + c: { u: "u123", h: "oc_group_1", t: "group", e: Date.now() + 60_000 }, + }), + }, + ], + }, + ], + }, + }; + + await feishuPlugin.actions?.handleAction?.({ + action: "send", + params: { to: "chat:oc_group_1", card }, + cfg, + accountId: undefined, + toolContext: {}, + } as never); + + expect(sendCardFeishuMock).toHaveBeenCalledWith( + expect.objectContaining({ + card, + }), + ); + }); + + it("rejects raw legacy card command payloads", async () => { + const legacyCard = { + schema: "2.0", + body: { + elements: [ + { + tag: "action", + actions: [ + { + tag: "button", + text: { tag: "plain_text", content: "Run /new" }, + value: { command: "/new" }, + }, + ], + }, + ], + }, + }; + + await expect( + feishuPlugin.actions?.handleAction?.({ + action: "send", + params: { to: "chat:oc_group_1", card: legacyCard }, + cfg, + accountId: undefined, + toolContext: {}, + } as never), + ).rejects.toThrow( + "Feishu card buttons that trigger text or commands must use structured interaction envelopes.", + ); + expect(sendCardFeishuMock).not.toHaveBeenCalled(); + }); + + it("rejects raw legacy card text payloads", async () => { + const legacyCard = { + schema: "2.0", + body: { + elements: [ + { + tag: "action", + actions: [ + { + tag: "button", + text: { tag: "plain_text", content: "Run /new" }, + value: { text: "/new" }, + }, + ], + }, + ], + }, + }; + + await expect( + feishuPlugin.actions?.handleAction?.({ + action: "send", + params: { to: "chat:oc_group_1", card: legacyCard }, + cfg, + accountId: undefined, + toolContext: {}, + } as never), + ).rejects.toThrow( + "Feishu card buttons that trigger text or commands must use structured interaction envelopes.", + ); + expect(sendCardFeishuMock).not.toHaveBeenCalled(); + }); + + it("allows non-button controls to carry text metadata values", async () => { + sendCardFeishuMock.mockResolvedValueOnce({ messageId: "om_card", chatId: "oc_group_1" }); + const card = { + schema: "2.0", + body: { + elements: [ + { + tag: "action", + actions: [ + { + tag: "select_static", + placeholder: { tag: "plain_text", content: "Pick one" }, + value: { text: "display-only metadata" }, + options: [ + { + text: { tag: "plain_text", content: "Option A" }, + value: "a", + }, + ], + }, + ], + }, + ], + }, + }; + + await feishuPlugin.actions?.handleAction?.({ + action: "send", + params: { to: "chat:oc_group_1", card }, + cfg, + accountId: undefined, + toolContext: {}, + } as never); + + expect(sendCardFeishuMock).toHaveBeenCalledWith( + expect.objectContaining({ + card, + }), + ); + }); + it("sends media through the outbound adapter", async () => { feishuOutboundSendMediaMock.mockResolvedValueOnce({ channel: "feishu",
extensions/feishu/src/channel.ts+34 −0 modified@@ -40,6 +40,7 @@ import { listEnabledFeishuAccounts, resolveDefaultFeishuAccountId, } from "./accounts.js"; +import { FEISHU_CARD_INTERACTION_VERSION } from "./card-interaction.js"; import { createFeishuClient } from "./client.js"; import { FeishuConfigSchema } from "./config-schema.js"; import { parseFeishuConversationId } from "./conversation-id.js"; @@ -60,6 +61,34 @@ function readFeishuMediaParam(params: Record<string, unknown>): string | undefin return media.trim() ? media : undefined; } +function isRecord(value: unknown): value is Record<string, unknown> { + return typeof value === "object" && value !== null; +} + +function hasLegacyFeishuCardCommandValue(actionValue: unknown): boolean { + return ( + isRecord(actionValue) && + actionValue.oc !== FEISHU_CARD_INTERACTION_VERSION && + (Boolean(typeof actionValue.command === "string" && actionValue.command.trim()) || + Boolean(typeof actionValue.text === "string" && actionValue.text.trim())) + ); +} + +function containsLegacyFeishuCardCommandValue(node: unknown): boolean { + if (Array.isArray(node)) { + return node.some((item) => containsLegacyFeishuCardCommandValue(item)); + } + if (!isRecord(node)) { + return false; + } + + if (node.tag === "button" && hasLegacyFeishuCardCommandValue(node.value)) { + return true; + } + + return Object.values(node).some((value) => containsLegacyFeishuCardCommandValue(value)); +} + const meta: ChannelMeta = { id: "feishu", label: "Feishu", @@ -526,6 +555,11 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResul const sendMedia = maybeSendMedia; let result; if (card) { + if (containsLegacyFeishuCardCommandValue(card)) { + throw new Error( + "Feishu card buttons that trigger text or commands must use structured interaction envelopes.", + ); + } result = await runtime.sendCardFeishu({ cfg: ctx.cfg, to,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/openclaw/openclaw/commit/81c45976db532324b5a0918a70decc19520dc354nvdPatchWEB
- github.com/advisories/GHSA-77w2-crqv-cmv3ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-77w2-crqv-cmv3nvdVendor AdvisoryWEB
- www.vulncheck.com/advisories/openclaw-dm-pairing-bypass-via-legacy-card-callbacksnvdThird Party Advisory
News mentions
0No linked articles in our index yet.