@@ -2,6 +2,33 @@ import { createLogger } from '@sim/logger'
22
33const logger = createLogger ( 'DisposableEmail' )
44
5+ /**
6+ * Forwarding services whose aliases are permanent and deliver to a real inbox. The upstream lists
7+ * carry some of them, or individual aliases under them, so they are exempted here rather than
8+ * edited out of the vendored file, which keeps a refresh from bringing them back.
9+ */
10+ const MAIL_RELAY_DOMAINS : ReadonlySet < string > = new Set ( [
11+ '33mail.com' ,
12+ 'abinemail.com' ,
13+ 'addy.io' ,
14+ 'addy.to' ,
15+ 'aleeas.com' ,
16+ 'anonaddy.com' ,
17+ 'anonaddy.me' ,
18+ 'duck.com' ,
19+ 'maskmy.id' ,
20+ 'mozmail.com' ,
21+ 'passinbox.com' ,
22+ 'passmail.com' ,
23+ 'passmail.net' ,
24+ 'privaterelay.appleid.com' ,
25+ 'relay.firefox.com' ,
26+ 'simplelogin.co' ,
27+ 'simplelogin.com' ,
28+ 'simplelogin.fr' ,
29+ 'slmail.me' ,
30+ ] )
31+
532let disposableDomains : Promise < ReadonlySet < string > > | undefined
633
734/**
@@ -35,14 +62,17 @@ function loadDisposableDomains(): Promise<ReadonlySet<string>> {
3562 * - https://github.com/disposable-email-domains/disposable-email-domains
3663 * `disposable_email_blocklist.conf` (CC0), at e2254bf7fc8dcf113f6028b21e263a352111490b
3764 *
38- * Refresh from both and review the diff for mainstream providers and mail relays (Gmail,
39- * Outlook, iCloud, Proton, DuckDuckGo, Firefox Relay, SimpleLogin, addy.io), none of which
40- * belong here.
65+ * Refresh from both, keeping the file an exact union of upstream, and review the diff for
66+ * mainstream providers (Gmail, Outlook, iCloud, Proton), none of which belong here.
4167 */
4268export async function isDisposableEmail ( email : string | null | undefined ) : Promise < boolean > {
4369 const domain = email ?. split ( '@' ) [ 1 ] ?. trim ( ) . toLowerCase ( )
4470 if ( ! domain ) return false
4571
72+ const labels = domain . split ( '.' )
73+ const candidates = labels . slice ( 0 , - 1 ) . map ( ( _ , i ) => labels . slice ( i ) . join ( '.' ) )
74+ if ( candidates . some ( ( candidate ) => MAIL_RELAY_DOMAINS . has ( candidate ) ) ) return false
75+
4676 let domains : ReadonlySet < string >
4777 try {
4878 domains = await loadDisposableDomains ( )
@@ -51,9 +81,5 @@ export async function isDisposableEmail(email: string | null | undefined): Promi
5181 return false
5282 }
5383
54- const labels = domain . split ( '.' )
55- for ( let i = 0 ; i < labels . length - 1 ; i ++ ) {
56- if ( domains . has ( labels . slice ( i ) . join ( '.' ) ) ) return true
57- }
58- return false
84+ return candidates . some ( ( candidate ) => domains . has ( candidate ) )
5985}
0 commit comments