fix: harden discord agent cid parsing (#29013) (thanks @Jacky1n7)

This commit is contained in:
Peter Steinberger
2026-03-02 03:07:30 +00:00
parent c14c17403e
commit c869ca4bbf
3 changed files with 51 additions and 16 deletions

View File

@@ -406,22 +406,21 @@ export function buildAgentSelectCustomId(componentId: string): string {
/**
* Parse agent component data from Carbon's parsed ComponentData
* Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
* Supports both legacy { componentId } and Components v2 { cid } payloads.
*/
function readParsedComponentId(data: ComponentData): unknown {
if (!data || typeof data !== "object") {
return undefined;
}
return "cid" in data
? (data as Record<string, unknown>).cid
: (data as Record<string, unknown>).componentId;
}
function parseAgentComponentData(data: ComponentData): {
componentId: string;
} | null {
if (!data || typeof data !== "object") {
return null;
}
// Carbon parses "key:componentId=xxx" into { componentId: "xxx" }
// Components v2 / other builders may use { cid: "xxx" } (e.g. occomp:cid=xxx).
const raw =
("cid" in data
? (data as Record<string, unknown>).cid
: (data as Record<string, unknown>).componentId) ??
(data as Record<string, unknown>).componentId;
const raw = readParsedComponentId(data);
const decodeSafe = (value: string): string => {
// `cid` values may be raw (not URI-encoded). Guard against malformed % sequences.
@@ -601,10 +600,7 @@ function parseDiscordComponentData(
if (!data || typeof data !== "object") {
return null;
}
const rawComponentId =
"cid" in data
? (data as { cid?: unknown }).cid
: (data as { componentId?: unknown }).componentId;
const rawComponentId = readParsedComponentId(data);
const rawModalId =
"mid" in data ? (data as { mid?: unknown }).mid : (data as { modalId?: unknown }).modalId;
let componentId = normalizeComponentId(rawComponentId);

View File

@@ -182,6 +182,44 @@ describe("agent components", () => {
expect(reply).toHaveBeenCalledWith({ content: "✓" });
expect(enqueueSystemEventMock).toHaveBeenCalled();
});
it("accepts cid payloads for agent button interactions", async () => {
const button = createAgentComponentButton({
cfg: createCfg(),
accountId: "default",
dmPolicy: "allowlist",
allowFrom: ["123456789"],
});
const { interaction, defer, reply } = createDmButtonInteraction();
await button.run(interaction, { cid: "hello_cid" } as ComponentData);
expect(defer).toHaveBeenCalledWith({ ephemeral: true });
expect(reply).toHaveBeenCalledWith({ content: "✓" });
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
expect.stringContaining("hello_cid"),
expect.any(Object),
);
});
it("keeps malformed percent cid values without throwing", async () => {
const button = createAgentComponentButton({
cfg: createCfg(),
accountId: "default",
dmPolicy: "allowlist",
allowFrom: ["123456789"],
});
const { interaction, defer, reply } = createDmButtonInteraction();
await button.run(interaction, { cid: "hello%2G" } as ComponentData);
expect(defer).toHaveBeenCalledWith({ ephemeral: true });
expect(reply).toHaveBeenCalledWith({ content: "✓" });
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
expect.stringContaining("hello%2G"),
expect.any(Object),
);
});
});
describe("discord component interactions", () => {