|
| 1 | +import { isValidEmailSyntax } from '@sim/utils/string' |
1 | 2 | import { describe, expect, it } from 'vitest' |
2 | | -import { quickValidateEmail } from '@/lib/messaging/email/validation' |
| 3 | +import { quickValidateEmail, validateAllowlistEntry } from '@/lib/messaging/email/validation' |
3 | 4 |
|
4 | 5 | describe('quickValidateEmail', () => { |
5 | 6 | it.concurrent('should validate a correct email', () => { |
@@ -145,3 +146,55 @@ describe('quickValidateEmail', () => { |
145 | 146 | expect(result.checks.domain).toBe(true) |
146 | 147 | }) |
147 | 148 | }) |
| 149 | + |
| 150 | +describe('isValidEmailSyntax', () => { |
| 151 | + it.concurrent('should only accept a bare domain when allowDomains is set', () => { |
| 152 | + expect(isValidEmailSyntax('@example.com')).toBe(false) |
| 153 | + expect(isValidEmailSyntax('@example.com', true)).toBe(true) |
| 154 | + }) |
| 155 | + |
| 156 | + it.concurrent('should accept single-label domains, which self-hosted deployments use', () => { |
| 157 | + expect(isValidEmailSyntax('@intranet', true)).toBe(true) |
| 158 | + expect(isValidEmailSyntax('@localhost', true)).toBe(true) |
| 159 | + }) |
| 160 | + |
| 161 | + it.concurrent('should reject bare domains the old startsWith("@") check let through', () => { |
| 162 | + for (const entry of ['@', '@-bad.com', '@bad-.com', '@example.com.', '@exa mple.com']) { |
| 163 | + expect(isValidEmailSyntax(entry, true)).toBe(false) |
| 164 | + } |
| 165 | + }) |
| 166 | + |
| 167 | + it.concurrent('should enforce the 254-character cap', () => { |
| 168 | + const at254 = `${'a'.repeat(242)}@example.com` |
| 169 | + const at255 = `${'a'.repeat(243)}@example.com` |
| 170 | + expect(at254).toHaveLength(254) |
| 171 | + expect(at255).toHaveLength(255) |
| 172 | + expect(isValidEmailSyntax(at254)).toBe(true) |
| 173 | + expect(isValidEmailSyntax(at255)).toBe(false) |
| 174 | + }) |
| 175 | + |
| 176 | + it.concurrent('should enforce the 63-character DNS label limit on bare domains', () => { |
| 177 | + expect(isValidEmailSyntax(`@${'a'.repeat(63)}.com`, true)).toBe(true) |
| 178 | + expect(isValidEmailSyntax(`@${'a'.repeat(64)}.com`, true)).toBe(false) |
| 179 | + }) |
| 180 | +}) |
| 181 | + |
| 182 | +describe('validateAllowlistEntry', () => { |
| 183 | + it.concurrent('should accept a valid address', () => { |
| 184 | + expect(validateAllowlistEntry('user@example.com')).toBeNull() |
| 185 | + }) |
| 186 | + |
| 187 | + it.concurrent('should waive address-level policy for bare domain entries', () => { |
| 188 | + expect(validateAllowlistEntry('@mailinator.com')).toBeNull() |
| 189 | + expect(validateAllowlistEntry('user@mailinator.com')).toBe( |
| 190 | + 'Disposable email addresses are not allowed' |
| 191 | + ) |
| 192 | + }) |
| 193 | + |
| 194 | + it.concurrent('should surface the underlying rejection reason', () => { |
| 195 | + expect(validateAllowlistEntry('notanemail')).toBe('Invalid email format') |
| 196 | + expect(validateAllowlistEntry('user..name@example.com')).toBe( |
| 197 | + 'Email contains suspicious patterns' |
| 198 | + ) |
| 199 | + }) |
| 200 | +}) |
0 commit comments