diff --git a/src/lib/spam-check.test.ts b/src/lib/spam-check.test.ts index b7e8bed6..fbc73806 100644 --- a/src/lib/spam-check.test.ts +++ b/src/lib/spam-check.test.ts @@ -98,8 +98,21 @@ describe("checkEmail", () => { expect(checkEmail(email).spam).toBe(false); }); + // Initials followed by digits is one of the most ordinary shapes a real + // address takes, and it used to be a hard block with no appeal: a reporter's + // years-old personal address was refused as "Email matches spam pattern". + // An address is not something a blocked signup can just pick differently. + it.each([ + ["abc123456789@example.com", "the shape reported as blocked"], + ["ab123456@example.com", "two initials and a digit run"], + ["jsm19850612@example.com", "initials and a date of birth"], + ["kp4155550143@example.com", "initials and a phone fragment"], + ["xy31415926535@example.com", "initials and digits of pi"], + ])("allows email: %s (%s)", (email) => { + expect(checkEmail(email).spam).toBe(false); + }); + it.each([ - ["ab123456@example.com", "letters then a long digit run"], ["x7f2q9k1m4z8p3w6r5t0@example.com", "long random local part"], ["someone+x7f2q9k1m4z8@gmail.com", "generated-looking tag"], ["someone@mailinator.com", "disposable domain"], diff --git a/src/lib/spam-check.ts b/src/lib/spam-check.ts index 550bb7f3..e8271f8e 100644 --- a/src/lib/spam-check.ts +++ b/src/lib/spam-check.ts @@ -151,8 +151,24 @@ const DISPOSABLE_DOMAINS = new Set([ "guerrillamail.org", "harakirimail.com", "mailforspam.com", ]); +/** + * Local parts that are random rather than chosen. + * + * There used to be a second rule here, `/^[a-z]{2,3}\d{6,}@/i`, meant to catch + * `ab123456@`. It caught people instead. Initials followed by digits is one of + * the most ordinary ways a real address is formed — a birth year, a phone + * fragment, digits of pi — and the shape alone says nothing about who typed it. + * It blocked a reporter's long-established personal Gmail address with nothing + * but "Email matches spam pattern" and no route back in. + * + * Unlike a username, an email address is not something a blocked signup can + * simply pick differently, so a false positive here is a closed door rather than + * an inconvenience. The same reasoning retired the plus-tag length rule below, + * and corroboration replaced shape in checkSpam (#531): what marks a generated + * address is randomness, which the length rule already covers, not the presence + * of digits after letters. + */ const SPAM_EMAIL_PATTERNS = [ - /^[a-z]{2,3}\d{6,}@/i, // ab123456@... /^[a-z0-9]{20,}@/i, // long random local part ];