fix: sonarqube denial of service issue

This commit is contained in:
2026-08-14 08:43:37 -05:00
parent 7a026b7e82
commit 81118245ce
2 changed files with 54 additions and 2 deletions
+27 -2
View File
@@ -10,9 +10,34 @@ export function formatPrice(cents: number): string {
return `$${(cents / 100).toFixed(2)}`;
}
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// RFC 5321 caps an address at 254 characters; reject anything longer up front so
// validation cost stays bounded regardless of what a client posts.
const MAX_EMAIL_LENGTH = 254;
// Both patterns are anchored single character classes with no overlapping
// alternatives, so they match in linear time. Splitting on '@' and '.' in code
// rather than in one combined pattern avoids the ambiguous (and backtracking)
// `[^\s@]+\.[^\s@]+` domain match.
const LOCAL_PART_RE = /^[^\s@]+$/;
const DOMAIN_LABEL_RE = /^[^\s@.]+$/;
export function isValidEmail(email: string): boolean {
return EMAIL_RE.test(email.trim());
const trimmed = email.trim();
if (trimmed.length === 0 || trimmed.length > MAX_EMAIL_LENGTH) {
return false;
}
const at = trimmed.indexOf('@');
if (at === -1 || at !== trimmed.lastIndexOf('@')) {
return false;
}
if (!LOCAL_PART_RE.test(trimmed.slice(0, at))) {
return false;
}
const labels = trimmed.slice(at + 1).split('.');
return labels.length >= 2 && labels.every((label) => DOMAIN_LABEL_RE.test(label));
}
export const MARKETING_CONSENT_TEXT =