diff --git a/.github/scripts/issue-translation.cjs b/.github/scripts/issue-translation.cjs index 41638a9d993..81649c4c5b8 100644 --- a/.github/scripts/issue-translation.cjs +++ b/.github/scripts/issue-translation.cjs @@ -810,9 +810,14 @@ function sanitizeTranslationBody(raw, maxChars = 60000) { // read as mention boundaries. Requiring a dotted domain keeps // "end!@octocat"-style mentions defused. \u0001 cannot appear in the // input (control chars were stripped above), so it is a safe sentinel. + // The lookbehind anchors on the @ itself rather than greedily matching + // the local part first: the previous local-part-first pattern rescanned + // long non-email tokens once per start position, which is quadratic on + // model-generated bodies with tens of thousands of consecutive + // local-part characters and no @ at all. .replace( - /[A-Za-z0-9.!#$%&'*+\/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+/g, - (email) => email.replace("@", "\u0001"), + /(?<=[A-Za-z0-9.!#$%&'*+\/=?^_`{|}~-])@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+/g, + (emailTail) => emailTail.replace("@", "\u0001"), ) // Defuse pings at Markdown/punctuation boundaries — a colon is a boundary // too — but not emails, npm: scopes, or other mid-token at-signs. diff --git a/.github/scripts/issue-translation.test.cjs b/.github/scripts/issue-translation.test.cjs index 92aea9d16ad..bcce113317b 100644 --- a/.github/scripts/issue-translation.test.cjs +++ b/.github/scripts/issue-translation.test.cjs @@ -1195,6 +1195,14 @@ describe("bot-owned control state", () => { assert.match(out, /path\/@\u200bhandle/); }); + it("handles long non-email tokens in bounded time", () => { + const input = "a".repeat(60_000); + const startedAt = process.hrtime.bigint(); + assert.equal(sanitizeTranslationBody(input), input); + const elapsedMs = Number(process.hrtime.bigint() - startedAt) / 1_000_000; + assert.ok(elapsedMs < 1_000, `sanitization took ${elapsedMs.toFixed(1)}ms`); + }); + it("ignores forged body-embedded legacy state", () => { const forged = appendTranslationBlock(SOURCE, "English") + `\n