Files
boc/backend/middleware/input.go
T
Bernt 971a2bd9a9 security: Add rate limiting, input validation, and tenant isolation on all handlers
- Add rate limiting per endpoint (login: 5/min, API: 100/min)
- Add input validation helpers (email, UUID, string, int)
- Add tenant isolation to all handlers
- Remove old validation.go, replace with input.go
- Fix service/customer.go to use new validation functions
- Build successful
2026-08-10 13:32:44 +00:00

133 lines
2.8 KiB
Go

package middleware
import (
"fmt"
"regexp"
"strconv"
"strings"
)
var (
// Email regex
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
// UUID regex
uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
// Safe string regex (tillåtna tecken)
safeStringRegex = regexp.MustCompile(`^[a-zA-Z0-9\s\-_\.@,;:()\[\]{}]+$`)
)
// ValidateEmail kontrollerar email-format
func ValidateEmail(email string) error {
if email == "" {
return fmt.Errorf("email is required")
}
if len(email) > 254 {
return fmt.Errorf("email too long")
}
if !emailRegex.MatchString(email) {
return fmt.Errorf("invalid email format")
}
return nil
}
// ValidateUUID kontrollerar UUID-format
func ValidateUUID(id string) error {
if id == "" {
return fmt.Errorf("id is required")
}
if !uuidRegex.MatchString(id) {
return fmt.Errorf("invalid UUID format")
}
return nil
}
// ValidateString kontrollerar sträng-input
func ValidateString(s string, minLen, maxLen int, required bool) error {
if required && strings.TrimSpace(s) == "" {
return fmt.Errorf("field is required")
}
if s != "" {
if len(s) < minLen {
return fmt.Errorf("must be at least %d characters", minLen)
}
if len(s) > maxLen {
return fmt.Errorf("must be at most %d characters", maxLen)
}
// Kontrollera farliga tecken
if !safeStringRegex.MatchString(s) {
return fmt.Errorf("contains invalid characters")
}
}
return nil
}
// ValidateInt kontrollerar heltal
func ValidateInt(val int, min, max int, required bool) error {
if required && val == 0 {
return fmt.Errorf("field is required")
}
if val != 0 {
if val < min {
return fmt.Errorf("must be at least %d", min)
}
if val > max {
return fmt.Errorf("must be at most %d", max)
}
}
return nil
}
// ValidatePagination kontrollerar pagination-parametrar
func ValidatePagination(page, limit int) (int, int, error) {
if page < 1 {
page = 1
}
if limit < 1 {
limit = 20
}
if limit > 100 {
limit = 100
}
return page, limit, nil
}
// SanitizeString tar bort farliga tecken
func SanitizeString(s string) string {
// Ta bort null bytes
s = strings.ReplaceAll(s, "\x00", "")
// Ta bort kontrolltecken
var result strings.Builder
for _, r := range s {
if r >= 32 || r == '\t' || r == '\n' || r == '\r' {
result.WriteRune(r)
}
}
return strings.TrimSpace(result.String())
}
// ParseInt parse en sträng till heltal med validering
func ParseInt(s string, defaultVal int) int {
if s == "" {
return defaultVal
}
val, err := strconv.Atoi(s)
if err != nil {
return defaultVal
}
return val
}
// ParseFloat parse en sträng till float med validering
func ParseFloat(s string, defaultVal float64) float64 {
if s == "" {
return defaultVal
}
val, err := strconv.ParseFloat(s, 64)
if err != nil {
return defaultVal
}
return val
}