package security import ( "net" "regexp" "strings" ) // Validator groups lightweight validation and sanitization helpers. type Validator struct { passwordMinLength int passwordRequireSpecial bool passwordRequireNumber bool } // NewValidator creates a validator with the configured password rules. func NewValidator(minLength int, requireSpecial, requireNumber bool) *Validator { return &Validator{ passwordMinLength: minLength, passwordRequireSpecial: requireSpecial, passwordRequireNumber: requireNumber, } } // ValidateEmail validates email format. func (v *Validator) ValidateEmail(email string) bool { if email == "" { return false } pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` matched, _ := regexp.MatchString(pattern, email) return matched } // ValidatePhone validates mainland China mobile numbers. func (v *Validator) ValidatePhone(phone string) bool { if phone == "" { return false } pattern := `^1[3-9]\d{9}$` matched, _ := regexp.MatchString(pattern, phone) return matched } // ValidateUsername validates usernames. func (v *Validator) ValidateUsername(username string) bool { if username == "" { return false } pattern := `^[a-zA-Z][a-zA-Z0-9_]{3,19}$` matched, _ := regexp.MatchString(pattern, username) return matched } // ValidatePassword validates passwords using the shared runtime policy. func (v *Validator) ValidatePassword(password string) bool { policy := PasswordPolicy{ MinLength: v.passwordMinLength, RequireSpecial: v.passwordRequireSpecial, RequireNumber: v.passwordRequireNumber, } return policy.Validate(password) == nil } // SanitizeSQL removes obviously dangerous SQL injection patterns using regex. // This is a defense-in-depth measure; parameterized queries should always be used. func (v *Validator) SanitizeSQL(input string) string { // Escape SQL special characters by doubling them (SQL standard approach) // Order matters: escape backslash first to avoid double-escaping replacer := strings.NewReplacer( `\`, `\\`, `'`, `''`, `"`, `""`, ) // Remove common SQL injection patterns that could bypass quoting dangerousPatterns := []string{ `;[\s]*--`, // SQL comment `/\*.*?\*/`, // Block comment (non-greedy) `\bxp_\w+`, // Extended stored procedures `\bexec[\s\(]`, // EXEC statements `\bsp_\w+`, // System stored procedures `\bwaitfor[\s]+delay`, // Time-based blind SQL injection `\bunion[\s]+select`, // UNION injection `\bdrop[\s]+table`, // DROP TABLE `\binsert[\s]+into`, // INSERT `\bupdate[\s]+\w+[\s]+set`, // UPDATE `\bdelete[\s]+from`, // DELETE } result := replacer.Replace(input) // Apply pattern removal for _, pattern := range dangerousPatterns { re := regexp.MustCompile(`(?i)` + pattern) // Case-insensitive result = re.ReplaceAllString(result, "") } return result } // SanitizeXSS removes obviously dangerous XSS patterns using regex. // This is a defense-in-depth measure; output encoding should always be used. func (v *Validator) SanitizeXSS(input string) string { // Remove dangerous tags and attributes using pattern matching dangerousPatterns := []struct { pattern string replaceAll bool }{ {`(?i)]*>.*?`, true}, // Script tags {`(?i)`, false}, // Closing script {`(?i)]*>.*?`, true}, // Iframe injection {`(?i)]*>.*?`, true}, // Object injection {`(?i)]*>.*?`, true}, // Embed injection {`(?i)]*>.*?`, true}, // Applet injection {`(?i)javascript\s*:`, false}, // JavaScript protocol {`(?i)vbscript\s*:`, false}, // VBScript protocol {`(?i)data\s*:`, false}, // Data URL protocol {`(?i)on\w+\s*=`, false}, // Event handlers {`(?i)]*>.*?`, true}, // Style injection } result := input for _, p := range dangerousPatterns { re := regexp.MustCompile(p.pattern) if p.replaceAll { result = re.ReplaceAllString(result, "") } else { result = re.ReplaceAllString(result, "") } } // Encode < and > to prevent tag construction result = strings.ReplaceAll(result, "<", "<") result = strings.ReplaceAll(result, ">", ">") // Restore entities if they were part of legitimate content result = strings.ReplaceAll(result, "<", "<") result = strings.ReplaceAll(result, ">", ">") return result } // ValidateURL validates a basic HTTP/HTTPS URL. func (v *Validator) ValidateURL(url string) bool { if url == "" { return false } pattern := `^https?://[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$` matched, _ := regexp.MatchString(pattern, url) return matched } // ValidateIP validates IPv4 or IPv6 addresses using net.ParseIP. // Supports all valid formats including compressed IPv6 (::1, fe80::1, etc.) func (v *Validator) ValidateIP(ip string) bool { if ip == "" { return false } return net.ParseIP(ip) != nil } // ValidateIPv4 validates IPv4 addresses only. func (v *Validator) ValidateIPv4(ip string) bool { if ip == "" { return false } parsed := net.ParseIP(ip) return parsed != nil && parsed.To4() != nil } // ValidateIPv6 validates IPv6 addresses only. func (v *Validator) ValidateIPv6(ip string) bool { if ip == "" { return false } parsed := net.ParseIP(ip) return parsed != nil && parsed.To4() == nil }