fix: email format validation with hyphens (#11761)
This PR updates the email validation regex to better handle use cases with hyphens. Changes: - Disallows domains starting or ending with a hyphen (`user@-example.com`, `user@example-.com`). - Allows domains with consecutive hyphens inside (`user@ex--ample.com`). - Allows multiple subdomains (`user@sub.domain.example.com`). - Adds `int test` coverage for multiple domain use case scenarios.
This commit is contained in:
@@ -192,12 +192,15 @@ export const email: EmailFieldValidation = (
|
||||
|
||||
/**
|
||||
* Disallows emails with double quotes (e.g., "user"@example.com, user@"example.com", "user@example.com")
|
||||
* Rejects spaces anywhere in the email (e.g., user @example.com)
|
||||
* Prevents consecutive dots (e.g., user..name@example.com)
|
||||
* Ensures a valid domain (e.g., rejects user@example, user@example..com)
|
||||
* Allows standard formats like user@example.com, user.name+alias@example.co.uk
|
||||
* Rejects spaces anywhere in the email (e.g., user @example.com, user@ example.com, user name@example.com)
|
||||
* Prevents consecutive dots in the local or domain part (e.g., user..name@example.com, user@example..com)
|
||||
* Disallows domains that start or end with a hyphen (e.g., user@-example.com, user@example-.com)
|
||||
* Allows standard email formats (e.g., user@example.com, user.name+alias@example.co.uk, user-name@example.org)
|
||||
* Allows domains with consecutive hyphens as long as they are not leading/trailing (e.g., user@ex--ample.com)
|
||||
* Supports multiple subdomains (e.g., user@sub.domain.example.com)
|
||||
*/
|
||||
const emailRegex = /^(?!.*\.\.)[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i
|
||||
const emailRegex =
|
||||
/^(?!.*\.\.)[\w.%+-]+@[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*\.[a-z]{2,}$/i
|
||||
|
||||
if ((value && !emailRegex.test(value)) || (!value && required)) {
|
||||
return t('validation:emailAddress')
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import type { FieldAffectingData, Payload, User } from 'payload'
|
||||
import type {
|
||||
BasePayload,
|
||||
EmailFieldValidation,
|
||||
FieldAffectingData,
|
||||
Payload,
|
||||
SanitizedConfig,
|
||||
User,
|
||||
} from 'payload'
|
||||
|
||||
import { jwtDecode } from 'jwt-decode'
|
||||
import path from 'path'
|
||||
import { email as emailValidation } from 'payload/shared'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
@@ -969,4 +977,59 @@ describe('Auth', () => {
|
||||
).rejects.toThrow('Token is either invalid or has expired.')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Email - format validation', () => {
|
||||
const mockT = jest.fn((key) => key) // Mocks translation function
|
||||
|
||||
const mockContext: Parameters<EmailFieldValidation>[1] = {
|
||||
// @ts-expect-error: Mocking context for email validation
|
||||
req: {
|
||||
payload: {
|
||||
collections: {} as Record<string, never>,
|
||||
config: {} as SanitizedConfig,
|
||||
} as unknown as BasePayload,
|
||||
t: mockT,
|
||||
},
|
||||
required: true,
|
||||
siblingData: {},
|
||||
blockData: {},
|
||||
data: {},
|
||||
path: ['email'],
|
||||
preferences: { fields: {} },
|
||||
}
|
||||
it('should allow standard formatted emails', () => {
|
||||
expect(emailValidation('user@example.com', mockContext)).toBe(true)
|
||||
expect(emailValidation('user.name+alias@example.co.uk', mockContext)).toBe(true)
|
||||
expect(emailValidation('user-name@example.org', mockContext)).toBe(true)
|
||||
expect(emailValidation('user@ex--ample.com', mockContext)).toBe(true)
|
||||
})
|
||||
|
||||
it('should not allow emails with double quotes', () => {
|
||||
expect(emailValidation('"user"@example.com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@"example.com"', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('"user@example.com"', mockContext)).toBe('validation:emailAddress')
|
||||
})
|
||||
|
||||
it('should not allow emails with spaces', () => {
|
||||
expect(emailValidation('user @example.com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@ example.com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user name@example.com', mockContext)).toBe('validation:emailAddress')
|
||||
})
|
||||
|
||||
it('should not allow emails with consecutive dots', () => {
|
||||
expect(emailValidation('user..name@example.com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@example..com', mockContext)).toBe('validation:emailAddress')
|
||||
})
|
||||
|
||||
it('should not allow emails with invalid domains', () => {
|
||||
expect(emailValidation('user@example', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@example..com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@example.c', mockContext)).toBe('validation:emailAddress')
|
||||
})
|
||||
|
||||
it('should not allow domains starting or ending with a hyphen', () => {
|
||||
expect(emailValidation('user@-example.com', mockContext)).toBe('validation:emailAddress')
|
||||
expect(emailValidation('user@example-.com', mockContext)).toBe('validation:emailAddress')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user