otpm/api/validator.go
“xHuPo” 5d370e1077 error
2025-05-27 17:44:24 +08:00

152 lines
No EOL
3.7 KiB
Go

package api
import (
"regexp"
"strings"
"github.com/go-playground/validator/v10"
)
// Validate is a global validator instance
var Validate = validator.New()
// RegisterCustomValidations registers custom validation functions
func RegisterCustomValidations() {
// Register custom validation for issuer
Validate.RegisterValidation("issuer", validateIssuer)
// Register custom validation for XSS prevention
Validate.RegisterValidation("no_xss", validateNoXSS)
// Register custom validation for OTP secret
Validate.RegisterValidation("otpsecret", validateOTPSecret)
}
// validateOTPSecret validates that the OTP secret is in valid base32 format
func validateOTPSecret(fl validator.FieldLevel) bool {
secret := fl.Field().String()
// Check if the secret is not empty
if secret == "" {
return false
}
// Check if the secret is in base32 format (A-Z, 2-7)
base32Regex := regexp.MustCompile(`^[A-Z2-7]+=*$`)
if !base32Regex.MatchString(secret) {
return false
}
// Check if the length is valid (must be at least 16 characters)
if len(secret) < 16 || len(secret) > 128 {
return false
}
return true
}
// validateIssuer validates that the issuer field contains only allowed characters
func validateIssuer(fl validator.FieldLevel) bool {
issuer := fl.Field().String()
// Empty issuer is valid (since it's optional)
if issuer == "" {
return true
}
// Allow alphanumeric characters, spaces, and common punctuation
issuerRegex := regexp.MustCompile(`^[a-zA-Z0-9\s\-_.,:;!?()[\]{}'"]+package api
import (
"regexp"
"strings"
"github.com/go-playground/validator/v10"
)
// Validate is a global validator instance
var Validate = validator.New()
// RegisterCustomValidations registers custom validation functions
func RegisterCustomValidations() {
// Register custom validation for issuer
Validate.RegisterValidation("issuer", validateIssuer)
// Register custom validation for XSS prevention
Validate.RegisterValidation("no_xss", validateNoXSS)
// Register custom validation for OTP secret
Validate.RegisterValidation("otpsecret", validateOTPSecret)
}
// validateOTPSecret validates that the OTP secret is in valid base32 format
func validateOTPSecret(fl validator.FieldLevel) bool {
secret := fl.Field().String()
// Check if the secret is not empty
if secret == "" {
return false
}
// Check if the secret is in base32 format (A-Z, 2-7)
base32Regex := regexp.MustCompile(`^[A-Z2-7]+=*$`)
if !base32Regex.MatchString(secret) {
return false
}
// Check if the length is valid (must be at least 16 characters)
if len(secret) < 16 || len(secret) > 128 {
return false
}
return true
}
)
if !issuerRegex.MatchString(issuer) {
return false
}
// Check length
if len(issuer) > 100 {
return false
}
return true
}
// validateNoXSS validates that the field doesn't contain potential XSS payloads
func validateNoXSS(fl validator.FieldLevel) bool {
value := fl.Field().String()
// Check for HTML encoding
if strings.Contains(value, "&#") ||
strings.Contains(value, "&lt;") ||
strings.Contains(value, "&gt;") {
return false
}
// Check for common XSS patterns
suspiciousPatterns := []*regexp.Regexp{
regexp.MustCompile(`(?i)<script[^>]*>.*?</script>`),
regexp.MustCompile(`(?i)javascript:`),
regexp.MustCompile(`(?i)data:text/html`),
regexp.MustCompile(`(?i)on\w+\s*=`),
regexp.MustCompile(`(?i)<\s*img[^>]*src\s*=`),
regexp.MustCompile(`(?i)<\s*iframe`),
regexp.MustCompile(`(?i)<\s*object`),
regexp.MustCompile(`(?i)<\s*embed`),
regexp.MustCompile(`(?i)<\s*style`),
regexp.MustCompile(`(?i)<\s*form`),
regexp.MustCompile(`(?i)<\s*applet`),
regexp.MustCompile(`(?i)<\s*meta`),
}
for _, pattern := range suspiciousPatterns {
if pattern.MatchString(value) {
return false
}
}
return true
}