Add E.164 input validation for phone numbers (FRE-4506)

- Extract phone validation to reusable utility (src/utils/phone-validation.ts)
- Use libphonenumber-js for strict E.164 format validation
- Normalize accepted numbers to canonical E.164 format
- Add 22 comprehensive validation tests covering valid/invalid formats
- Update existing tests to use valid E.164 test numbers
- All 56 tests passing

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Senior Engineer
2026-04-29 21:21:41 -04:00
committed by Michael Freno
parent 3ad030a412
commit 76d431e1ec
3 changed files with 147 additions and 15 deletions

View File

@@ -2,6 +2,7 @@ import { PrismaClient, SpamFeedback, SpamRule, SpamAuditLog } from '@prisma/clie
import { FieldEncryptionService } from '@shieldai/db';
import { spamConfig, spamFeatureFlags } from '../config/spamshield.config';
import { CircuitBreaker, CircuitBreakerError, CircuitState, CircuitBreakerMetrics } from '../circuit-breaker';
import { validatePhoneNumber as validateE164 } from '../utils/phone-validation';
const prisma = new PrismaClient() as PrismaClient & {
spamFeedback: {
@@ -269,11 +270,7 @@ export class SpamShieldService {
}
private validatePhoneNumber(phoneNumber: string): string {
if (phoneNumber.length < spamConfig.minPhoneNumberLength ||
phoneNumber.length > spamConfig.maxPhoneNumberLength) {
throw new Error(`Invalid phone number format: ${phoneNumber}`);
}
return phoneNumber;
return validateE164(phoneNumber);
}
private async getActiveRules(): Promise<Array<{ id: string; pattern: string }>> {

View File

@@ -0,0 +1,25 @@
import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js';
export class PhoneNumberValidationError extends Error {
constructor(public readonly originalInput: string) {
super(
`Invalid E.164 phone number format: ${originalInput}. Expected format: +[country code][number] (e.g., +14155552671)`
);
this.name = 'PhoneNumberValidationError';
}
}
export function validatePhoneNumber(phoneNumber: string): string {
const trimmed = phoneNumber.trim();
if (!isValidPhoneNumber(trimmed)) {
throw new PhoneNumberValidationError(phoneNumber);
}
const parsed = parsePhoneNumber(trimmed);
if (!parsed || !parsed.number) {
throw new PhoneNumberValidationError(phoneNumber);
}
return parsed.number;
}