diff --git a/app/chatpal-search/client/template/admin.js b/app/chatpal-search/client/template/admin.js index 703fabc2e76..ab3210bc249 100644 --- a/app/chatpal-search/client/template/admin.js +++ b/app/chatpal-search/client/template/admin.js @@ -6,11 +6,10 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { settings } from '../../../settings'; import { hasRole } from '../../../authorization'; import { dispatchToastMessage } from '../../../../client/lib/toast'; +import { validateEmail } from '../../../../lib/emailValidator'; Template.ChatpalAdmin.onCreated(function() { - const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - - this.validateEmail = (email) => re.test(email.toLowerCase()); + this.validateEmail = validateEmail; this.apiKey = new ReactiveVar(); diff --git a/app/lib/server/lib/validateEmailDomain.js b/app/lib/server/lib/validateEmailDomain.js index 85df94bed53..37dbd2c6735 100644 --- a/app/lib/server/lib/validateEmailDomain.js +++ b/app/lib/server/lib/validateEmailDomain.js @@ -4,9 +4,9 @@ import { Meteor } from 'meteor/meteor'; import { emailDomainDefaultBlackList } from './defaultBlockedDomainsList'; import { settings } from '../../../settings/server'; +import { validateEmail } from '../../../../lib/emailValidator'; const dnsResolveMx = Meteor.wrapAsync(dns.resolveMx); -const emailValidationRegex = /^[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])?)*$/; let emailDomainBlackList = []; let emailDomainWhiteList = []; @@ -29,7 +29,7 @@ settings.watch('Accounts_AllowedDomainsList', function(value) { }); export const validateEmailDomain = function(email) { - if (!emailValidationRegex.test(email)) { + if (!validateEmail(email)) { throw new Meteor.Error('error-invalid-email', `Invalid email ${ email }`, { function: 'RocketChat.validateEmailDomain', email }); } diff --git a/app/livechat/server/lib/Helper.js b/app/livechat/server/lib/Helper.js index 96f3bcd7251..6038afc13f9 100644 --- a/app/livechat/server/lib/Helper.js +++ b/app/livechat/server/lib/Helper.js @@ -16,9 +16,9 @@ import { sendNotification } from '../../../lib/server'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { queueInquiry, saveQueueInquiry } from './QueueManager'; import { OmnichannelSourceType } from '../../../../definition/IRoom'; +import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; const logger = new Logger('LivechatHelper'); -const emailValidationRegex = /^[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])?)*$/; export const allowAgentSkipQueue = (agent) => { check(agent, Match.ObjectIncluding({ @@ -555,7 +555,7 @@ export const updateDepartmentAgents = (departmentId, agents, departmentEnabled) }; export const validateEmail = (email) => { - if (!emailValidationRegex.test(email)) { + if (!validatorFunc(email)) { throw new Meteor.Error('error-invalid-email', `Invalid email ${ email }`, { function: 'Livechat.validateEmail', email }); } return true; diff --git a/app/mailer/server/api.ts b/app/mailer/server/api.ts index c59cd2151fd..5b97bf6b740 100644 --- a/app/mailer/server/api.ts +++ b/app/mailer/server/api.ts @@ -10,6 +10,7 @@ import { escapeHTML } from '@rocket.chat/string-helpers'; import { settings } from '../../settings/server'; import { ISetting } from '../../../definition/ISetting'; import { replaceVariables } from './replaceVariables'; +import { validateEmail } from '../../../lib/emailValidator'; let contentHeader: string | undefined; let contentFooter: string | undefined; @@ -130,9 +131,7 @@ settings.watchMultiple(['Email_Header', 'Email_Footer'], () => { body = inlinecss(`${ contentHeader } {{body}} ${ contentFooter }`); }); -export const rfcMailPatternWithName = /^(?:.*<)?([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])?)*)(?:>?)$/; - -export const checkAddressFormat = (adresses: string | string[]): boolean => ([] as string[]).concat(adresses).every((address) => rfcMailPatternWithName.test(address)); +export const checkAddressFormat = (adresses: string | string[]): boolean => ([] as string[]).concat(adresses).every((address) => validateEmail(address)); export const sendNoWrap = ({ to, diff --git a/client/views/setupWizard/steps/AdminUserInformationStep.js b/client/views/setupWizard/steps/AdminUserInformationStep.js index dffa56414b6..76e3c7ae5cf 100644 --- a/client/views/setupWizard/steps/AdminUserInformationStep.js +++ b/client/views/setupWizard/steps/AdminUserInformationStep.js @@ -11,6 +11,7 @@ import { useAutoFocus, useUniqueId } from '@rocket.chat/fuselage-hooks'; import React, { useMemo, useState, useEffect } from 'react'; import { callbacks } from '../../../../app/callbacks/lib/callbacks'; +import { validateEmail as emailValidator } from '../../../../lib/emailValidator'; import { useMethod } from '../../../contexts/ServerContext'; import { useSessionDispatch } from '../../../contexts/SessionContext'; import { useSetting } from '../../../contexts/SettingsContext'; @@ -61,7 +62,6 @@ function AdminUserInformationStep({ step, title, active }) { () => new RegExp(`^${regexpForUsernameValidation}$`), [regexpForUsernameValidation], ); - const emailRegExp = useMemo(() => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i, []); const [name, setName] = useState(''); const [username, setUsername] = useState(''); @@ -83,8 +83,8 @@ function AdminUserInformationStep({ step, title, active }) { }, [username, usernameRegExp]); useEffect(() => { - validateEmail(email && emailRegExp.test(email)); - }, [email, emailRegExp]); + validateEmail(emailValidator(email)); + }, [email]); const t = useTranslation(); diff --git a/lib/emailValidator.ts b/lib/emailValidator.ts new file mode 100644 index 00000000000..8020f42d712 --- /dev/null +++ b/lib/emailValidator.ts @@ -0,0 +1,12 @@ +export const validateEmail = (email: string, options: { style: string } = { style: 'basic' }): boolean => { + const basicEmailRegex = /^[^@]+@[^@]+$/; + const rfcEmailRegex = /^[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])?)*$/; + + switch (options.style) { + case 'rfc': + return rfcEmailRegex.test(email); + case 'basic': + default: + return basicEmailRegex.test(email); + } +}; diff --git a/server/startup/initialData.js b/server/startup/initialData.js index 668426a7d3c..fa98b24d9c0 100644 --- a/server/startup/initialData.js +++ b/server/startup/initialData.js @@ -8,6 +8,7 @@ import { Users, Rooms } from '../../app/models/server'; import { settings } from '../../app/settings/server'; import { checkUsernameAvailability, addUserToDefaultChannels } from '../../app/lib/server'; import { Settings } from '../../app/models/server/raw'; +import { validateEmail } from '../../lib/emailValidator'; Meteor.startup(async function() { if (settings.get('Show_Setup_Wizard') === 'pending' && !Rooms.findOneById('GENERAL')) { @@ -66,9 +67,7 @@ Meteor.startup(async function() { console.log(`Name: ${ adminUser.name }`.green); if (process.env.ADMIN_EMAIL) { - const re = /^[^@].*@[^@]+$/i; - - if (re.test(process.env.ADMIN_EMAIL)) { + if (validateEmail(process.env.ADMIN_EMAIL)) { if (!Users.findOneByEmailAddress(process.env.ADMIN_EMAIL)) { adminUser.emails = [{ address: process.env.ADMIN_EMAIL,