fix(whatsapp): avoid mentions inside unterminated inline code (#129281)

* fix(whatsapp): avoid mentions inside unterminated inline code

* fix(whatsapp): protect mentions across soft code line breaks

* fix(whatsapp): honor escaped multiline code delimiter runs
This commit is contained in:
Peter Steinberger
2026-08-25 08:15:51 -07:00
committed by GitHub
parent 8a93f681c0
commit d53ea9cf4d
3 changed files with 72 additions and 4 deletions
@@ -16,11 +16,25 @@ describe("resolveWhatsAppOutboundMentions", () => {
});
});
it("rewrites phone-number tokens to LID mention text without device suffixes", () => {
it.each([
{ text: "ping @+5511976136970", expected: "ping @277038292303944" },
{
text: "literal \\` ping @+5511976136970",
expected: "literal \\` ping @277038292303944",
},
{
text: "literal \\\\\\` ping @+5511976136970",
expected: "literal \\\\\\` ping @277038292303944",
},
{
text: "inside ``notify ` @+5511976136970`` then @+5511976136970",
expected: "inside ``notify ` @+5511976136970`` then @277038292303944",
},
])("rewrites visible phone-number mentions to LIDs: $text", ({ text, expected }) => {
expect(
resolveWhatsAppOutboundMentions({
chatJid: "120363000000000000@g.us",
text: "ping @+5511976136970",
text,
participants: [
{
id: "277038292303944:2@lid",
@@ -29,7 +43,7 @@ describe("resolveWhatsAppOutboundMentions", () => {
],
}),
).toEqual({
text: "ping @277038292303944",
text: expected,
mentionedJids: ["277038292303944@lid"],
});
});
@@ -115,6 +129,30 @@ describe("resolveWhatsAppOutboundMentions", () => {
});
});
it.each([
"Run `notify @+5511976136970",
"Run `notify\n@+5511976136970",
"Run ``notify ` @+5511976136970",
"Run ``notify ` @+5511976136970``",
"literal \\\\` inside @+5511976136970",
])(
"does not rewrite or mention phone numbers inside balanced or unterminated inline code: %j",
(text) => {
expect(
resolveWhatsAppOutboundMentions({
chatJid: "120363000000000000@g.us",
text,
participants: [
{
id: "277038292303944@lid",
phoneNumber: "5511976136970@s.whatsapp.net",
},
],
}),
).toEqual({ text, mentionedJids: [] });
},
);
it("does not mention numeric prefixes inside longer tokens", () => {
expect(
resolveWhatsAppOutboundMentions({
@@ -16,7 +16,7 @@ export type WhatsAppOutboundMentionResolution = {
};
const CODE_FENCE_RE = /```[\s\S]*?```/g;
const INLINE_CODE_RE = /`[^`\n]+`/g;
const INLINE_CODE_RE = /(?<=(?:^|[^\\])(?:\\\\)*)(`+)[\s\S]*?(?:(?<!`)\1(?!`)|$)/g;
const OUTBOUND_MENTION_RE = /@(\+?\d+)/g;
const KNOWN_USER_JID_RE = /^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted|lid|hosted\.lid|c\.us)$/i;
const PHONE_JID_DOMAIN_RE = /^(s\.whatsapp\.net|hosted|c\.us)$/i;
@@ -12,6 +12,7 @@ import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-run
import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { prepareWhatsAppOutboundMedia } from "../outbound-media-contract.js";
import { markdownToWhatsApp } from "../text-runtime.js";
import { resolveWhatsAppOutboundMentions } from "./outbound-mentions.js";
import { createWebSendApi } from "./send-api.js";
import { normalizeWhatsAppSendResult, type WhatsAppSendResult } from "./send-result.js";
@@ -329,6 +330,35 @@ describe("createWebSendApi", () => {
});
});
it.each([
{ messageText: "Run `notify @15551234567" },
{ messageText: "Run `notify\n@15551234567" },
{ messageText: "Run ``notify ` @15551234567" },
{ messageText: "literal \\` ping @15551234567", nativeMention: true },
{ messageText: "literal \\\\` inside @15551234567" },
])(
"only sends native mentions for visible phone numbers outside inline code: $messageText",
async ({ messageText, nativeMention }) => {
api = createWebSendApi({
sock: { sendMessage, sendPresenceUpdate },
defaultAccountId: "main",
resolveOutboundMentions: ({ jid, text }) =>
resolveWhatsAppOutboundMentions({
chatJid: jid,
text,
participants: [{ id: "15551234567@s.whatsapp.net" }],
}),
});
await api.sendMessage("120363000000000000@g.us", markdownToWhatsApp(messageText));
expect(sendMessage).toHaveBeenCalledWith("120363000000000000@g.us", {
text: messageText,
...(nativeMention ? { mentions: ["15551234567@s.whatsapp.net"] } : {}),
});
},
);
it("supports image media with caption", async () => {
const payload = Buffer.from("img");
await api.sendMessage("+1555", "cap", payload, "image/jpeg");