diff --git a/.github/scripts/issue-translation.cjs b/.github/scripts/issue-translation.cjs index c7d546bc23..bd977f0243 100644 --- a/.github/scripts/issue-translation.cjs +++ b/.github/scripts/issue-translation.cjs @@ -805,9 +805,10 @@ function sanitizeTranslationBody(raw, maxChars = 60000) { .split(MARKER).join("") .split(END_MARKER).join("") .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "") - // Defuse pings only: @login / @org/team — not emails, scopes, or decorators. + // Defuse pings at Markdown/punctuation boundaries, but not emails, scopes, + // or other mid-token at-signs. .replace( - /(^|[\s(])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,38})(?:\/[A-Za-z0-9._-]+)?)/g, + /(^|[^A-Za-z0-9._%+:-])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,38})(?:\/[A-Za-z0-9._-]+)?)/g, "$1@\u200b$2", ) .trim() diff --git a/.github/scripts/issue-translation.test.cjs b/.github/scripts/issue-translation.test.cjs index f6c754fb9e..66e5add042 100644 --- a/.github/scripts/issue-translation.test.cjs +++ b/.github/scripts/issue-translation.test.cjs @@ -1168,9 +1168,14 @@ describe("bot-owned control state", () => { assert.equal(decision.reason, "rate_limited_interval"); }); - it("defuses mention-shaped tokens without rewriting emails or mid-token at-signs", () => { - const out = sanitizeTranslationBody("see @octocat and user@example.com and npm:@scope"); + it("defuses mention-shaped tokens at Markdown and punctuation boundaries", () => { + const out = sanitizeTranslationBody( + "see @octocat, comma,@team, [@user], >@org/team, user@example.com, npm:@scope", + ); assert.match(out, /@\u200boctocat/); + assert.match(out, /,@\u200bteam/); + assert.match(out, /\[@\u200buser\]/); + assert.match(out, />@\u200borg\/team/); assert.ok(out.includes("user@example.com")); assert.ok(out.includes("npm:@scope")); });