/** * WiFi Credentials Validation Utility * * Provides comprehensive validation for WiFi SSID and password * following IEEE 802.11 standards and common router implementations. */ export interface WiFiValidationResult { valid: boolean; errors: string[]; warnings: string[]; } export interface WiFiCredentials { ssid: string; password: string; } /** * Validate WiFi SSID * * Rules: * - Length: 1-32 characters (IEEE 802.11 standard) * - Can contain: letters, numbers, spaces, and special characters * - Cannot be empty * - Should not contain control characters */ export function validateSSID(ssid: string): WiFiValidationResult { const errors: string[] = []; const warnings: string[] = []; // Check for empty SSID if (!ssid || ssid.trim().length === 0) { errors.push('Network name (SSID) cannot be empty'); return { valid: false, errors, warnings }; } // Check length (IEEE 802.11 standard: max 32 bytes) // Note: UTF-8 characters can be multiple bytes const byteLength = new Blob([ssid]).size; if (byteLength > 32) { errors.push('Network name is too long (max 32 bytes)'); } // Check for control characters (ASCII 0-31) if (/[\x00-\x1F]/.test(ssid)) { errors.push('Network name contains invalid control characters'); } // Warnings for potentially problematic SSIDs if (ssid.startsWith(' ') || ssid.endsWith(' ')) { warnings.push('Network name has leading or trailing spaces - this may cause connection issues'); } if (/\s{2,}/.test(ssid)) { warnings.push('Network name has multiple consecutive spaces'); } return { valid: errors.length === 0, errors, warnings, }; } /** * Validate WiFi Password * * Rules based on WPA/WPA2/WPA3 standards: * - WPA/WPA2-PSK: 8-63 characters (ASCII) * - Open networks: no password required * - WEP: 5 or 13 characters (ASCII) or 10/26 hex digits (deprecated) * * Additional checks: * - Should not be empty for secured networks * - Should not contain control characters * - Common weak passwords warning */ export function validatePassword( password: string, authType?: string ): WiFiValidationResult { const errors: string[] = []; const warnings: string[] = []; // Open networks don't need password if (authType === 'Open') { return { valid: true, errors, warnings }; } // Check for empty password on secured networks if (!password || password.length === 0) { errors.push('Password is required for secured networks'); return { valid: false, errors, warnings }; } // WPA/WPA2/WPA3 validation (most common) if (!authType || authType.includes('WPA')) { // Length check: 8-63 characters if (password.length < 8) { errors.push('Password must be at least 8 characters for WPA/WPA2/WPA3 networks'); } if (password.length > 63) { errors.push('Password cannot exceed 63 characters for WPA/WPA2/WPA3 networks'); } // Check for ASCII only (non-ASCII can cause issues) if (!/^[\x20-\x7E]*$/.test(password)) { warnings.push('Password contains non-ASCII characters - this may cause issues on some devices'); } } // WEP validation (deprecated but still exists) if (authType === 'WEP') { const isValidWEP = password.length === 5 || // 40-bit WEP password.length === 13 || // 104-bit WEP (password.length === 10 && /^[0-9A-Fa-f]+$/.test(password)) || // 40-bit hex (password.length === 26 && /^[0-9A-Fa-f]+$/.test(password)); // 104-bit hex if (!isValidWEP) { errors.push('WEP password must be 5 or 13 ASCII characters, or 10/26 hexadecimal digits'); } warnings.push('WEP is deprecated and insecure - consider upgrading your router to WPA2/WPA3'); } // Check for control characters if (/[\x00-\x1F]/.test(password)) { errors.push('Password contains invalid control characters'); } // Weak password warnings if (password.length < 12 && !errors.length) { warnings.push('Password is weak - consider using at least 12 characters'); } // Common weak passwords const weakPasswords = [ 'password', '12345678', 'qwerty123', 'abc123456', 'password1', 'password123', '11111111', '00000000' ]; if (weakPasswords.includes(password.toLowerCase())) { warnings.push('This is a commonly used weak password'); } // Check for only numbers if (/^\d+$/.test(password) && password.length === 8) { warnings.push('Password contains only numbers - consider adding letters and symbols'); } return { valid: errors.length === 0, errors, warnings, }; } /** * Validate complete WiFi credentials (SSID + password) */ export function validateWiFiCredentials( ssid: string, password: string, authType?: string ): WiFiValidationResult { const ssidResult = validateSSID(ssid); const passwordResult = validatePassword(password, authType); return { valid: ssidResult.valid && passwordResult.valid, errors: [...ssidResult.errors, ...passwordResult.errors], warnings: [...ssidResult.warnings, ...passwordResult.warnings], }; } /** * Check if password is strong enough for production use * More strict than basic validation */ export function isStrongPassword(password: string): boolean { if (password.length < 12) return false; const hasUppercase = /[A-Z]/.test(password); const hasLowercase = /[a-z]/.test(password); const hasNumber = /\d/.test(password); const hasSpecial = /[^A-Za-z0-9]/.test(password); // Require at least 3 of 4 character types const typesCount = [hasUppercase, hasLowercase, hasNumber, hasSpecial].filter(Boolean).length; return typesCount >= 3; } /** * Sanitize WiFi credentials before sending to device * Trims whitespace and performs basic cleanup */ export function sanitizeWiFiCredentials(credentials: WiFiCredentials): WiFiCredentials { return { ssid: credentials.ssid.trim(), password: credentials.password, // Don't trim password - spaces might be intentional }; } /** * Get user-friendly error message for validation result */ export function getValidationErrorMessage(result: WiFiValidationResult): string { if (result.valid) return ''; if (result.errors.length > 0) { return result.errors[0]; // Return first error } return 'Invalid WiFi credentials'; } /** * Get combined error and warning message */ export function getValidationMessage(result: WiFiValidationResult): string { const messages: string[] = []; if (result.errors.length > 0) { messages.push(...result.errors); } if (result.warnings.length > 0) { messages.push(...result.warnings); } return messages.join('\n'); }