/** * Input validation and sanitization utilities */ export const sanitizeString = (input: string): string => { // Remove potential XSS vectors while preserving legitimate content return input .replace(/)<[^<]*)*<\/script>/gi, '') .replace(/on\w+\s*=\s*["'][^"']*["']/gi, '') .replace(/javascript:/gi, '') .trim(); }; export const validateEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; export const validatePhone = (phone: string): boolean => { // Accepts various phone formats const phoneRegex = /^[\d\s\-\(\)\+]+$/; return phoneRegex.test(phone) && phone.replace(/\D/g, '').length >= 10; }; export const validateNumber = (value: string): number | null => { const parsed = parseFloat(value); if (isNaN(parsed)) return null; return parsed; }; export const validatePositiveNumber = (value: string): number | null => { const num = validateNumber(value); if (num === null || num < 0) return null; return num; }; export const validateRequired = (value: string): boolean => { return value.trim().length > 0; }; export const validateInvoiceNumber = (invoiceNumber: string, existingNumbers: string[]): boolean => { if (!validateRequired(invoiceNumber)) return false; // Check uniqueness const sanitized = sanitizeString(invoiceNumber); return !existingNumbers.some(num => num === sanitized); }; export const generateInvoiceNumber = (existingNumbers: string[]): string => { const year = new Date().getFullYear(); let counter = 1; let invoiceNum: string; do { invoiceNum = `INV-${year}-${String(counter).padStart(3, '0')}`; counter++; } while (existingNumbers.includes(invoiceNum)); return invoiceNum; };