package security import ( "html" "regexp" "strings" "github.com/microcosm-cc/bluemonday" ) // XSSPolicy defines the sanitization policy type XSSPolicy struct { policy *bluemonday.Policy } // NewXSSPolicy creates a new XSS sanitization policy func NewXSSPolicy() *XSSPolicy { p := bluemonday.UGCPolicy() // Allow basic formatting but strip scripts and dangerous attributes p.AllowAttrs("href", "target", "rel").OnElements("a") p.AllowAttrs("src", "alt", "width", "height").OnElements("img") p.AllowElements("p", "br", "strong", "em", "u", "h1", "h2", "h3", "h4", "h5", "h6") p.AllowElements("ul", "ol", "li") return &XSSPolicy{policy: p} } // SanitizeHTML sanitizes HTML input, allowing safe tags func (x *XSSPolicy) SanitizeHTML(input string) string { return x.policy.Sanitize(input) } // SanitizeText completely escapes HTML entities for safe text display func (x *XSSPolicy) SanitizeText(input string) string { return html.EscapeString(input) } // SanitizeEmail sanitizes input for use in email contexts func (x *XSSPolicy) SanitizeEmail(input string) string { // For emails, we want to allow basic formatting but be strict return strings.TrimSpace(x.SanitizeText(input)) } // SanitizeURL sanitizes URL input func (x *XSSPolicy) SanitizeURL(input string) string { // Remove javascript: and data: schemes dangerous := regexp.MustCompile(`(?i)^(javascript|data|vbscript):`) if dangerous.MatchString(input) { return "" } return strings.TrimSpace(input) } // SanitizePhoneNumber sanitizes phone number input func (x *XSSPolicy) SanitizePhoneNumber(input string) string { // Remove any HTML tags and trim whitespace clean := regexp.MustCompile(`<[^>]*>`).ReplaceAllString(input, "") return strings.TrimSpace(clean) } // ValidateAndSanitizeInput validates and sanitizes user input func (x *XSSPolicy) ValidateAndSanitizeInput(input string, fieldType string) (string, error) { if strings.TrimSpace(input) == "" { return "", nil } switch fieldType { case "text": return x.SanitizeText(input), nil case "html": return x.SanitizeHTML(input), nil case "email": return x.SanitizeEmail(input), nil case "url": return x.SanitizeURL(input), nil case "phone": return x.SanitizePhoneNumber(input), nil default: return x.SanitizeText(input), nil } }