fix: keep an escaped quote in a pasted display name from splitting off a recipient

This commit is contained in:
Linus Rath
2026-10-31 03:49:55 +01:00
parent 8bb7b60728
commit ae2eb46b29
2 changed files with 27 additions and 2 deletions
+17
View File
@@ -38,6 +38,23 @@ describe('splitRecipients', () => {
it('keeps RFC 5322 groups as one entry', () => {
expect(splitRecipients('Team: a@x.com, b@y.com;, c@z.com')).toEqual(['Team: a@x.com, b@y.com;', 'c@z.com']);
});
it('does not take an escaped quote in a display name as the closing one', () => {
expect(splitRecipients('"Support\\", ceo@corp.example, \\"x" <s@x.com>, b@y.com'))
.toEqual(['"Support\\", ceo@corp.example, \\"x" <s@x.com>', 'b@y.com']);
});
it('does not take an escaped quote as closing when an unclosed angle bracket looks ahead', () => {
expect(splitRecipients('John <j@x.com, "a\\"> b" <b@y.com>')).toEqual(['John <j@x.com', '"a\\"> b" <b@y.com>']);
});
});
describe('splitPastedRecipients with an escaped quote (composer paste)', () => {
it('adds no extra recipient from inside a display name', () => {
const { valid, invalid } = splitPastedRecipients('"Support\\", ceo@corp.example, \\"x" <s@x.com>, b@y.com');
expect(valid.map((r) => r.email)).toEqual(['s@x.com', 'b@y.com']);
expect(invalid).toEqual([]);
});
});
describe('parseRecipient', () => {
+10 -2
View File
@@ -58,7 +58,9 @@ function angleRunCloses(value: string, from: number): boolean {
let inQuotes = false;
for (let i = from + 1; i < value.length; i++) {
const ch = value[i];
if (ch === '"') inQuotes = !inQuotes;
// A quoted-pair (`\"`) never closes the quote.
if (inQuotes && ch === '\\') i++;
else if (ch === '"') inQuotes = !inQuotes;
else if (inQuotes) continue;
else if (ch === '>') return true;
else if (ch === '<') return false;
@@ -84,7 +86,13 @@ export function splitRecipients(value: string, separators = ','): string[] {
let inGroup = false;
for (let i = 0; i < value.length; i++) {
const ch = value[i];
if (ch === '"') {
if (inQuotes && ch === '\\' && i + 1 < value.length) {
// A quoted-pair: a `"` in a display name is written `\"`. Taking it as
// the closing quote let a name like `Support", ceo@corp.example, "x`
// split into an extra recipient (webmail lib/email-composer-utils.ts).
current += ch + value[i + 1];
i++;
} else if (ch === '"') {
inQuotes = !inQuotes;
current += ch;
} else if (ch === '<' && !inQuotes) {