// Package validation provides shared input validation utilities. package validation import ( "net" "net/mail" "strings" ) // ValidateEmail validates an email address using Go's standard mail package. // Returns an error message if the email is invalid, or an empty string if valid. // This provides consistent email validation across all services. func ValidateEmail(email string) string { email = strings.TrimSpace(email) if email == "" { return "Email address is required" } // Use Go's mail package for proper RFC 5322 email validation if _, err := mail.ParseAddress(email); err != nil { return "Please enter a valid email address" } return "" } // IsValidEmail returns true if the email is valid, false otherwise. // This is a convenience wrapper around ValidateEmail for simple boolean checks. func IsValidEmail(email string) bool { return ValidateEmail(email) == "" } // MaskEmail masks an email address for safe logging. // Example: "john.doe@example.com" becomes "jo***@example.com" // This prevents PII exposure in logs while still allowing identification. func MaskEmail(email string) string { email = strings.TrimSpace(email) if email == "" { return "***" } parts := strings.Split(email, "@") if len(parts) != 2 { return "***" } localPart := parts[0] domain := parts[1] // Mask local part based on length var maskedLocal string switch { case len(localPart) <= 1: maskedLocal = "*" case len(localPart) <= 3: maskedLocal = localPart[:1] + "***" default: maskedLocal = localPart[:2] + "***" } return maskedLocal + "@" + domain } // MaskIP masks an IP address for safe logging. // IPv4 example: "192.168.1.100" becomes "192.168.1.***" // IPv6 example: "2001:db8::1" becomes "2001:db8::***" // This prevents PII exposure in logs while still allowing network identification. func MaskIP(ip string) string { ip = strings.TrimSpace(ip) if ip == "" { return "***" } // Remove port if present (format: "IP:port" or "[IPv6]:port") host, _, err := net.SplitHostPort(ip) if err == nil { ip = host } // Parse to determine if IPv4 or IPv6 parsedIP := net.ParseIP(ip) if parsedIP == nil { return "***" } // Check if IPv4 if ipv4 := parsedIP.To4(); ipv4 != nil { // Mask last octet: 192.168.1.100 -> 192.168.1.*** parts := strings.Split(ip, ".") if len(parts) == 4 { return parts[0] + "." + parts[1] + "." + parts[2] + ".***" } return "***" } // IPv6: mask last segment // Example: 2001:db8:85a3::8a2e:370:7334 -> 2001:db8:85a3::8a2e:370:*** parts := strings.Split(ip, ":") if len(parts) > 1 { parts[len(parts)-1] = "***" return strings.Join(parts, ":") } return "***" }