feat(security): add blocked domains and strict TLD validation

- Adds `BLOCKED_DOMAINS` list to reject disposable/invalid email domains.
- Enforces TLD length >= 2 chars in `isValidEmail`.
- Updates tests to cover new validation rules.

Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot]
2026-01-30 04:56:33 +00:00
parent 15c4b88535
commit c9877db3bb
3 changed files with 50 additions and 8 deletions

View File

@@ -29,14 +29,14 @@ describe('Security Utils', () => {
describe('isValidEmail', () => {
it('accepts valid email addresses', () => {
expect(isValidEmail('test@example.com')).toBe(true);
expect(isValidEmail('test@valid-domain.com')).toBe(true);
expect(isValidEmail('john.doe@sub.domain.co.uk')).toBe(true);
expect(isValidEmail('user+tag@example.com')).toBe(true);
expect(isValidEmail('user+tag@valid-domain.com')).toBe(true);
});
it('rejects invalid email formats', () => {
expect(isValidEmail('plainaddress')).toBe(false);
expect(isValidEmail('@example.com')).toBe(false);
expect(isValidEmail('@valid-domain.com')).toBe(false);
expect(isValidEmail('user@')).toBe(false);
expect(isValidEmail('user@.com')).toBe(false);
expect(isValidEmail('user@com')).toBe(false); // Missing dot in domain part (simple regex might allow, but strict one requires dot)
@@ -54,13 +54,31 @@ describe('Security Utils', () => {
});
it('accepts emails with apostrophes', () => {
expect(isValidEmail("user'name@example.com")).toBe(true);
expect(isValidEmail("o'connor@example.com")).toBe(true);
expect(isValidEmail("user'name@valid-domain.com")).toBe(true);
expect(isValidEmail("o'connor@valid-domain.com")).toBe(true);
});
it('rejects emails with whitespace', () => {
expect(isValidEmail('user @example.com')).toBe(false);
expect(isValidEmail('user@ example.com')).toBe(false);
});
it('rejects short TLDs (less than 2 chars)', () => {
expect(isValidEmail('user@domain.c')).toBe(false);
expect(isValidEmail('user@domain.1')).toBe(false);
});
it('rejects blocked/disposable domains', () => {
expect(isValidEmail('user@example.com')).toBe(false);
expect(isValidEmail('test@test.com')).toBe(false);
expect(isValidEmail('spam@mailinator.com')).toBe(false);
expect(isValidEmail('bot@yopmail.com')).toBe(false);
expect(isValidEmail('temp@temp-mail.org')).toBe(false);
});
it('rejects blocked domains regardless of case', () => {
expect(isValidEmail('user@EXAMPLE.COM')).toBe(false);
expect(isValidEmail('user@MaIlInAtOr.CoM')).toBe(false);
});
});
});

View File

@@ -17,9 +17,22 @@ export function sanitizeInput(input: string): string {
.replace(/'/g, "&#039;");
}
// Common disposable email providers and invalid domains
const BLOCKED_DOMAINS = new Set([
"example.com",
"test.com",
"mailinator.com",
"yopmail.com",
"temp-mail.org",
"guerrillamail.com",
"10minutemail.com",
"trashmail.com",
]);
/**
* Validates an email address format securely.
* Rejects inputs containing dangerous characters like <, >, or whitespace.
* Also validates domain validity (blocked domains, TLD length).
*
* @param email - The email string to validate.
* @returns True if the email is valid and safe, false otherwise.
@@ -30,7 +43,13 @@ export function isValidEmail(email: string): boolean {
// @ : Literal @
// [^\s@<>]+ : Domain part - no whitespace, @, <, or >
// \. : Literal .
// [^\s@<>]+ : TLD part - no whitespace, @, <, or >
const emailRegex = /^[^\s@<>,"`]+@[^\s@<>,"`]+\.[^\s@<>,"`]+$/;
return emailRegex.test(email);
// [^\s@<>]+ : TLD part - no whitespace, @, <, or > (min 2 chars)
const emailRegex = /^[^\s@<>,"`]+@[^\s@<>,"`]+\.[^\s@<>,"`]{2,}$/;
if (!emailRegex.test(email)) {
return false;
}
// Check against blocked domains
const domain = email.split("@")[1].toLowerCase();
return !BLOCKED_DOMAINS.has(domain);
}