monorepo/cloud/maplepress-backend/pkg/validation/helpers.go

120 lines
3.1 KiB
Go

package validation
import (
"fmt"
"net/http"
"strconv"
)
// ValidatePathUUID validates a UUID path parameter
// CWE-20: Improper Input Validation
func ValidatePathUUID(r *http.Request, paramName string) (string, error) {
value := r.PathValue(paramName)
if value == "" {
return "", fmt.Errorf("%s is required", paramName)
}
validator := NewValidator()
if err := validator.ValidateUUID(value, paramName); err != nil {
return "", err
}
return value, nil
}
// ValidatePathSlug validates a slug path parameter
// CWE-20: Improper Input Validation
func ValidatePathSlug(r *http.Request, paramName string) (string, error) {
value := r.PathValue(paramName)
if value == "" {
return "", fmt.Errorf("%s is required", paramName)
}
validator := NewValidator()
if err := validator.ValidateSlug(value, paramName); err != nil {
return "", err
}
return value, nil
}
// ValidatePathInt validates an integer path parameter
// CWE-20: Improper Input Validation
func ValidatePathInt(r *http.Request, paramName string) (int64, error) {
valueStr := r.PathValue(paramName)
if valueStr == "" {
return 0, fmt.Errorf("%s is required", paramName)
}
value, err := strconv.ParseInt(valueStr, 10, 64)
if err != nil {
return 0, fmt.Errorf("%s must be a valid integer", paramName)
}
if value <= 0 {
return 0, fmt.Errorf("%s must be greater than 0", paramName)
}
return value, nil
}
// ValidatePagination validates pagination query parameters
// Returns limit and offset with defaults and bounds checking
func ValidatePagination(r *http.Request, defaultLimit int) (limit int, offset int, err error) {
limit = defaultLimit
offset = 0
// Validate limit
if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
parsedLimit, err := strconv.Atoi(limitStr)
if err != nil || parsedLimit <= 0 || parsedLimit > 100 {
return 0, 0, fmt.Errorf("limit must be between 1 and 100")
}
limit = parsedLimit
}
// Validate offset
if offsetStr := r.URL.Query().Get("offset"); offsetStr != "" {
parsedOffset, err := strconv.Atoi(offsetStr)
if err != nil || parsedOffset < 0 {
return 0, 0, fmt.Errorf("offset must be >= 0")
}
offset = parsedOffset
}
return limit, offset, nil
}
// ValidateSortField validates sort field against whitelist
// CWE-89: SQL Injection prevention via whitelist
func ValidateSortField(r *http.Request, allowedFields []string) (string, error) {
sortBy := r.URL.Query().Get("sort_by")
if sortBy == "" {
return "", nil // Optional field
}
for _, allowed := range allowedFields {
if sortBy == allowed {
return sortBy, nil
}
}
return "", fmt.Errorf("invalid sort_by field (allowed: %v)", allowedFields)
}
// ValidateQueryEmail validates an email query parameter
// CWE-20: Improper Input Validation
func ValidateQueryEmail(r *http.Request, paramName string) (string, error) {
email := r.URL.Query().Get(paramName)
if email == "" {
return "", fmt.Errorf("%s is required", paramName)
}
emailValidator := NewEmailValidator()
normalizedEmail, err := emailValidator.ValidateAndNormalize(email, paramName)
if err != nil {
return "", err
}
return normalizedEmail, nil
}