diff --git a/.changeset/fix-default-blocked-email-domains.md b/.changeset/fix-default-blocked-email-domains.md new file mode 100644 index 0000000000000..5d6bdbe299aff --- /dev/null +++ b/.changeset/fix-default-blocked-email-domains.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes the default blocked email domains list (`Accounts_UseDefaultBlockedDomainsList`) being ignored during registration unless a custom `Accounts_BlockedDomainsList` was also set. The two settings are now independent, so the built-in list of disposable/throwaway domains is enforced on a stock install as intended. diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index 23cf27a6152e4..d0625c39affac 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -51,6 +51,7 @@ export default { '/server/api/v1/middlewares/*.spec.ts', '/server/lib/cloud/version-check/**/*.spec.ts', '/server/lib/auth-providers/apple/**.spec.ts', + '/server/lib/isEmailDomainBlocked.spec.ts', ], coveragePathIgnorePatterns: ['/node_modules/'], }, diff --git a/apps/meteor/server/lib/isEmailDomainBlocked.spec.ts b/apps/meteor/server/lib/isEmailDomainBlocked.spec.ts new file mode 100644 index 0000000000000..25c6cd0089fca --- /dev/null +++ b/apps/meteor/server/lib/isEmailDomainBlocked.spec.ts @@ -0,0 +1,32 @@ +import { isEmailDomainBlocked } from './isEmailDomainBlocked'; + +// `0-mail.com` is present in the built-in default list; `gmail.com` is not. +describe('isEmailDomainBlocked', () => { + it('blocks a default-listed domain when the default list is enabled, even with an empty custom list', () => { + // Regression: previously the default list was only consulted when the + // custom list was non-empty, so on a stock install this returned false. + expect(isEmailDomainBlocked('0-mail.com', [], true)).toBe(true); + }); + + it('does not block a default-listed domain when the default list is disabled', () => { + expect(isEmailDomainBlocked('0-mail.com', [], false)).toBe(false); + }); + + it('blocks a domain on the custom list', () => { + expect(isEmailDomainBlocked('evil.example', ['evil.example'], false)).toBe(true); + }); + + it('does not block a domain that is on neither list', () => { + expect(isEmailDomainBlocked('gmail.com', ['evil.example'], true)).toBe(false); + }); + + it('blocks a default-listed domain regardless of case', () => { + // A valid email can carry an upper-case domain (`user@0-MAIL.COM`); the + // lower-cased default entry must still match. + expect(isEmailDomainBlocked('0-MAIL.COM', [], true)).toBe(true); + }); + + it('matches the custom list case-insensitively', () => { + expect(isEmailDomainBlocked('EVIL.example', ['evil.EXAMPLE'], false)).toBe(true); + }); +}); diff --git a/apps/meteor/server/lib/isEmailDomainBlocked.ts b/apps/meteor/server/lib/isEmailDomainBlocked.ts new file mode 100644 index 0000000000000..bbd2365b58b52 --- /dev/null +++ b/apps/meteor/server/lib/isEmailDomainBlocked.ts @@ -0,0 +1,23 @@ +import { emailDomainDefaultBlackList } from './defaultBlockedDomainsList'; + +/** + * Decide whether an email domain should be blocked at registration. + * + * The admin-configured blocklist (`Accounts_BlockedDomainsList`) and the + * built-in default list (`Accounts_UseDefaultBlockedDomainsList`) are + * independent controls, so the default list must apply on its own even when no + * custom domain has been configured. Previously both checks were gated behind + * the custom list being non-empty, so on a stock install the default list was + * never consulted. + * + * Domains are case-insensitive, so the incoming domain is lowercased before + * comparison (the default list is stored lowercase); otherwise `user@0-MAIL.COM` + * would slip past a listed `0-mail.com`. + */ +export const isEmailDomainBlocked = (emailDomain: string, blockList: string[], useDefaultList: boolean): boolean => { + const domain = emailDomain.toLowerCase(); + return ( + (blockList.length > 0 && blockList.some((blocked) => blocked.toLowerCase() === domain)) || + (useDefaultList && emailDomainDefaultBlackList.includes(domain)) + ); +}; diff --git a/apps/meteor/server/lib/validateEmailDomain.js b/apps/meteor/server/lib/validateEmailDomain.js index 8cd8ea9d3c1ef..14cfca4557766 100644 --- a/apps/meteor/server/lib/validateEmailDomain.js +++ b/apps/meteor/server/lib/validateEmailDomain.js @@ -4,7 +4,7 @@ import util from 'node:util'; import { validateEmail } from '@rocket.chat/tools'; import { Meteor } from 'meteor/meteor'; -import { emailDomainDefaultBlackList } from './defaultBlockedDomainsList'; +import { isEmailDomainBlocked } from './isEmailDomainBlocked'; import { settings } from '../settings'; const dnsResolveMx = util.promisify(dns.resolveMx); @@ -50,11 +50,7 @@ export const validateEmailDomain = async function (email) { function: 'RocketChat.validateEmailDomain', }); } - if ( - emailDomainBlackList.length && - (emailDomainBlackList.indexOf(emailDomain) !== -1 || - (settings.get('Accounts_UseDefaultBlockedDomainsList') && emailDomainDefaultBlackList.indexOf(emailDomain) !== -1)) - ) { + if (isEmailDomainBlocked(emailDomain, emailDomainBlackList, settings.get('Accounts_UseDefaultBlockedDomainsList'))) { throw new Meteor.Error('error-email-domain-blacklisted', 'The email domain is blacklisted', { function: 'RocketChat.validateEmailDomain', });