test: add quote reply regression coverage

This commit is contained in:
Codex CLI Audit 2026-03-08 15:28:38 -04:00 committed by Joey Krug
parent 75505e49db
commit 55798ae175
3 changed files with 106 additions and 0 deletions

View File

@ -345,6 +345,49 @@ describe("signal quote reply handling", () => {
expect(String(ctx?.Body ?? "")).toContain("[Quoting +15550002222 id:1700000000001]");
});
it("does not poison the quote-author cache from attacker-controlled quote metadata", async () => {
const handler = createQuoteHandler();
await handler(
createSignalReceiveEvent({
sourceNumber: "+15550002222",
sourceName: "Bob",
timestamp: 1700000000001,
dataMessage: {
message: "Forwarding this",
groupInfo: { groupId: "g1", groupName: "Test Group" },
quote: {
id: 1700000000000,
authorNumber: "+15550009999",
text: "Mallory wrote this",
},
},
}),
);
capturedCtx = undefined;
await handler(
createSignalReceiveEvent({
sourceNumber: "+15550003333",
sourceName: "Alice",
timestamp: 1700000000002,
dataMessage: {
message: "Replying to Bob",
groupInfo: { groupId: "g1", groupName: "Test Group" },
quote: {
id: 1700000000001,
text: "Forwarding this",
},
},
}),
);
const ctx = getCapturedCtx();
expect(ctx?.ReplyToSender).toBe("+15550002222");
expect(String(ctx?.Body ?? "")).toContain("[Quoting +15550002222 id:1700000000001]");
});
it("resolves cached uuid senders with a uuid: prefix", async () => {
const handler = createQuoteHandler();
const senderUuid = "123e4567-e89b-12d3-a456-426614174000";

View File

@ -2,10 +2,33 @@ import { describe, expect, it } from "vitest";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
import {
applyReplyThreading,
filterMessagingToolMediaDuplicates,
shouldSuppressMessagingToolReplies,
} from "./reply-payloads.js";
describe("applyReplyThreading", () => {
it("treats whitespace-only replyToId as unset so implicit replies still apply", () => {
const result = applyReplyThreading({
payloads: [{ text: "hello", replyToId: " \n\t " }],
replyToMode: "all",
currentMessageId: "123",
});
expect(result).toEqual([{ text: "hello", replyToId: "123" }]);
});
it("preserves explicit null replyToId as do-not-reply", () => {
const result = applyReplyThreading({
payloads: [{ text: "hello", replyToId: null }],
replyToMode: "all",
currentMessageId: "123",
});
expect(result).toEqual([{ text: "hello", replyToId: null }]);
});
});
describe("filterMessagingToolMediaDuplicates", () => {
it("strips mediaUrl when it matches sentMediaUrls", () => {
const result = filterMessagingToolMediaDuplicates({

View File

@ -3,6 +3,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
import { markdownToSignalTextChunks } from "../../../extensions/signal/src/format.js";
import {
signalOutbound,
slackOutbound,
telegramOutbound,
whatsappOutbound,
} from "../../../test/channel-outbounds.js";
@ -1027,6 +1028,45 @@ describe("deliverOutboundPayloads", () => {
);
});
it("keeps inherited Slack thread context across all payloads", async () => {
const sendSlack = vi
.fn()
.mockResolvedValueOnce({ messageId: "sl1", channelId: "C123" })
.mockResolvedValueOnce({ messageId: "sl2", channelId: "C123" });
setActivePluginRegistry(
createTestRegistry([
{
pluginId: "slack",
plugin: createOutboundTestPlugin({ id: "slack", outbound: slackOutbound }),
source: "test",
},
]),
);
await deliverOutboundPayloads({
cfg: { channels: { slack: {} } },
channel: "slack",
to: "C123",
payloads: [{ text: "first" }, { text: "second" }],
replyToId: "thread-123",
deps: { sendSlack },
});
expect(sendSlack).toHaveBeenCalledTimes(2);
expect(sendSlack).toHaveBeenNthCalledWith(
1,
"C123",
"first",
expect.objectContaining({ threadTs: "thread-123" }),
);
expect(sendSlack).toHaveBeenNthCalledWith(
2,
"C123",
"second",
expect.objectContaining({ threadTs: "thread-123" }),
);
});
it("passes normalized payload to onError", async () => {
const sendWhatsApp = vi.fn().mockRejectedValue(new Error("boom"));
const onError = vi.fn();