Frontend (jobs/new): - Replace isValidCNPJ (checksum algorithm) with isValidDocument: accepts any tax document with 5–30 alphanumeric chars (CNPJ, EIN, VAT, etc.) - Add cleanPhone(): strips formatting chars (dashes, spaces, parens) and keeps only digits + optional leading '+'; replaces cleanDigits+prepend - Phone sent as '+5511999998888' if user typed '+55...', or '11999998888' if no country code was provided — no '+' blindly prepended anymore - Company document sent stripped of all non-alphanumeric before API call - Update label placeholder from '00.000.000/0000-00' to 'CNPJ, EIN, VAT...' - Rename error key invalidCnpj → invalidDocument in all 3 locales (pt, en, es) Backend (create_company use case): - Add SanitizePhone() to utils/sanitizer.go: strips all non-digit chars except a leading '+'; '(11) 99999-8888' → '11999998888' - Apply SanitizePhone to input.Phone before persisting to DB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
3 KiB
Go
111 lines
3 KiB
Go
package utils
|
|
|
|
import (
|
|
"html"
|
|
"regexp"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// Sanitizer provides input sanitization utilities
|
|
type Sanitizer struct {
|
|
// Max lengths for common fields
|
|
MaxNameLength int
|
|
MaxDescriptionLength int
|
|
MaxEmailLength int
|
|
}
|
|
|
|
// DefaultSanitizer returns a sanitizer with default settings
|
|
func DefaultSanitizer() *Sanitizer {
|
|
return &Sanitizer{
|
|
MaxNameLength: 255,
|
|
MaxDescriptionLength: 10000,
|
|
MaxEmailLength: 320,
|
|
}
|
|
}
|
|
|
|
// SanitizeString escapes HTML and trims whitespace
|
|
func (s *Sanitizer) SanitizeString(input string) string {
|
|
if input == "" {
|
|
return ""
|
|
}
|
|
// Trim whitespace
|
|
result := strings.TrimSpace(input)
|
|
// Escape HTML entities to prevent XSS
|
|
result = html.EscapeString(result)
|
|
return result
|
|
}
|
|
|
|
// SanitizeName sanitizes a name field
|
|
func (s *Sanitizer) SanitizeName(input string) string {
|
|
sanitized := s.SanitizeString(input)
|
|
if utf8.RuneCountInString(sanitized) > s.MaxNameLength {
|
|
runes := []rune(sanitized)
|
|
sanitized = string(runes[:s.MaxNameLength])
|
|
}
|
|
return sanitized
|
|
}
|
|
|
|
// SanitizeEmail sanitizes and validates email format
|
|
func (s *Sanitizer) SanitizeEmail(input string) string {
|
|
sanitized := strings.TrimSpace(strings.ToLower(input))
|
|
if utf8.RuneCountInString(sanitized) > s.MaxEmailLength {
|
|
return ""
|
|
}
|
|
return sanitized
|
|
}
|
|
|
|
// SanitizeDescription sanitizes long text fields
|
|
func (s *Sanitizer) SanitizeDescription(input string) string {
|
|
sanitized := s.SanitizeString(input)
|
|
if utf8.RuneCountInString(sanitized) > s.MaxDescriptionLength {
|
|
runes := []rune(sanitized)
|
|
sanitized = string(runes[:s.MaxDescriptionLength])
|
|
}
|
|
return sanitized
|
|
}
|
|
|
|
// SanitizeSlug creates a URL-safe slug
|
|
func (s *Sanitizer) SanitizeSlug(input string) string {
|
|
// Convert to lowercase
|
|
result := strings.ToLower(strings.TrimSpace(input))
|
|
// Replace spaces with hyphens
|
|
result = strings.ReplaceAll(result, " ", "-")
|
|
// Remove non-alphanumeric characters except hyphens
|
|
reg := regexp.MustCompile(`[^a-z0-9-]`)
|
|
result = reg.ReplaceAllString(result, "")
|
|
// Remove multiple consecutive hyphens
|
|
reg = regexp.MustCompile(`-+`)
|
|
result = reg.ReplaceAllString(result, "-")
|
|
// Trim hyphens from ends
|
|
result = strings.Trim(result, "-")
|
|
return result
|
|
}
|
|
|
|
// StripHTML removes all HTML tags from input
|
|
func StripHTML(input string) string {
|
|
reg := regexp.MustCompile(`<[^>]*>`)
|
|
return reg.ReplaceAllString(input, "")
|
|
}
|
|
|
|
// SanitizePhone cleans a phone number, keeping only digits and an optional
|
|
// leading '+' (international dialing prefix). Removes all formatting characters
|
|
// such as spaces, dashes, parentheses, and dots.
|
|
//
|
|
// Examples:
|
|
//
|
|
// "+55 (11) 99999-8888" → "+5511999998888"
|
|
// "(11) 99999-8888" → "11999998888"
|
|
// "+1-800-555-0100" → "+18005550100"
|
|
func SanitizePhone(phone string) string {
|
|
phone = strings.TrimSpace(phone)
|
|
if phone == "" {
|
|
return ""
|
|
}
|
|
hasPlus := strings.HasPrefix(phone, "+")
|
|
digitsOnly := regexp.MustCompile(`\D`).ReplaceAllString(phone, "")
|
|
if hasPlus {
|
|
return "+" + digitsOnly
|
|
}
|
|
return digitsOnly
|
|
}
|