Files
cv-site/VALIDATION_QUICK_REFERENCE.md
T
juanatsap 92dffe8c60 feat: add comprehensive testing infrastructure and security hardening
- Enhanced CI/CD pipeline with coverage reporting, benchmarks, and artifact uploads
- Implemented rate limiter IP validation with proxy support and spoofing protection
- Added extensive Makefile test targets for coverage, benchmarks, and continuous testing
- Expanded middleware chain with request validation, size limits, and suspicious activity logging
2025-11-11 21:43:12 +00:00

6.4 KiB

Input Validation Quick Reference

For Developers: How to Validate User Input

Basic Usage

import "github.com/juanatsap/cv-site/internal/validator"

// 1. Validate language parameter
lang, err := validator.ValidateLanguage(r.URL.Query().Get("lang"))
if err != nil {
    // Log and reject
    log.Printf("SECURITY: Invalid input - IP: %s, Value: %q", getIP(r), lang)
    http.Error(w, "Invalid parameter", http.StatusBadRequest)
    return
}

// 2. Validate general query parameter
value, err := validator.ValidateQueryParam(
    r.URL.Query().Get("param"),
    50,                                    // max length
    regexp.MustCompile(`^[a-zA-Z0-9]+$`), // pattern (optional)
)

// 3. Check file paths (prevent path traversal)
if !validator.IsValidFilePath(filePath) {
    http.Error(w, "Invalid file path", http.StatusBadRequest)
    return
}

// 4. Sanitize user input
clean := validator.SanitizeInput(userInput)

// 5. Check for suspicious patterns
if validator.ContainsSuspiciousPatterns(input) {
    log.Printf("SECURITY: Attack detected - %q", input)
    http.Error(w, "Invalid input", http.StatusBadRequest)
    return
}

Middleware Usage

Apply to Specific Routes

// Protect PDF endpoint
protectedHandler := middleware.MaxRequestSize(1024 * 1024)(
    http.HandlerFunc(handler.ExportPDF),
)
mux.Handle("/export/pdf", protectedHandler)

Global Application (Already Applied)

// Full security stack (in main.go)
handler := middleware.Recovery(
    middleware.Logger(
        middleware.LogSuspiciousActivity(
            middleware.SanitizeHeaders(
                middleware.ValidateQueryStrings(
                    middleware.ValidateRequestPath(
                        middleware.MaxRequestSize(10 * 1024 * 1024)(
                            middleware.SecurityHeaders(mux),
                        ),
                    ),
                ),
            ),
        ),
    ),
)

Common Validation Patterns

Language Selection

lang, err := validator.ValidateLanguage(r.URL.Query().Get("lang"))
if err != nil {
    HandleError(w, r, BadRequestError("Invalid language. Supported: en, es"))
    return
}

File Upload

// 1. Check content type
allowedTypes := []string{"image/png", "image/jpeg", "application/pdf"}
if !validator.ValidateContentType(r.Header.Get("Content-Type"), allowedTypes) {
    http.Error(w, "Invalid file type", http.StatusBadRequest)
    return
}

// 2. Sanitize filename
filename := validator.SanitizeFilename(r.FormValue("filename"))

// 3. Validate path
if !validator.IsValidFilePath(filename) {
    http.Error(w, "Invalid filename", http.StatusBadRequest)
    return
}

Search Query

// Alphanumeric only, max 100 chars
query, err := validator.ValidateQueryParam(
    r.URL.Query().Get("q"),
    100,
    regexp.MustCompile(`^[a-zA-Z0-9 ]+$`),
)
if err != nil {
    http.Error(w, "Invalid search query", http.StatusBadRequest)
    return
}

Security Checklist for New Endpoints

  • Validate all query parameters
  • Validate all form inputs
  • Check file paths if accessing files
  • Sanitize user-provided content
  • Check for suspicious patterns
  • Log rejected inputs
  • Return generic error messages (don't expose internals)
  • Add rate limiting if resource-intensive
  • Test with attack vectors

Testing Your Validation

# Test script
#!/bin/bash

# Valid request
curl -v "http://localhost:1999/endpoint?param=valid"

# Invalid characters
curl -v "http://localhost:1999/endpoint?param=<script>alert(1)</script>"

# Path traversal
curl -v "http://localhost:1999/endpoint?param=../../etc/passwd"

# SQL injection
curl -v "http://localhost:1999/endpoint?param=' OR '1'='1"

# Null byte
curl -v "http://localhost:1999/endpoint?param=test%00admin"

# Excessive length
curl -v "http://localhost:1999/endpoint?param=$(python3 -c 'print("a"*5000)')"

Error Handling Best Practices

DO

// Generic error message
if err := validator.ValidateLanguage(lang); err != nil {
    log.Printf("SECURITY: Invalid input - IP: %s, Value: %q", ip, lang)
    http.Error(w, "Invalid parameter", http.StatusBadRequest)
    return
}

DON'T

// Exposing validation details to attacker
if err := validator.ValidateLanguage(lang); err != nil {
    http.Error(w, fmt.Sprintf("Invalid language: %v", err), http.StatusBadRequest)
    return
}

Security Logging

Log Format

log.Printf("SECURITY: <event> - IP: %s, Path: %s, Value: %q",
    getClientIP(r), r.URL.Path, suspiciousValue)

What to Log

  • Rejected inputs
  • Attack patterns detected
  • Suspicious activity
  • IP addresses
  • Timestamps
  • Don't log sensitive data (passwords, tokens, PII)

Common Mistakes to Avoid

  1. Not validating all inputs

    // ❌ BAD: Direct use without validation
    lang := r.URL.Query().Get("lang")
    
    // ✅ GOOD: Always validate
    lang, err := validator.ValidateLanguage(r.URL.Query().Get("lang"))
    
  2. Client-side validation only

    • Always validate on server-side
    • Client validation is for UX, not security
  3. Blacklist instead of whitelist

    // ❌ BAD: Trying to block bad values
    if lang == "admin" || lang == "root" { reject() }
    
    // ✅ GOOD: Only allow known-good values
    if lang != "en" && lang != "es" { reject() }
    
  4. Not sanitizing output

    • Even validated input should be sanitized for output
    • Use HTML escaping for HTML output
    • Use proper encoding for JSON
  5. Trusting referer/origin headers

    • Headers can be spoofed
    • Use secure tokens for sensitive operations

When to Add New Validation

Add validation whenever:

  • Accepting user input (query params, forms, headers)
  • Constructing file paths from user input
  • Building commands or queries from user input
  • Accepting file uploads
  • Processing URL parameters
  • Handling HTTP headers

Performance Considerations

Validation is fast (<1ms per request), but:

  • Cache validated results when possible
  • Don't re-validate same input multiple times
  • Use simple checks first (length) before complex (regex)
  • Consider async validation for non-critical paths

Questions?

  1. Check internal/validator/validator_test.go for examples
  2. Review SECURITY_VALIDATION_REPORT.md for detailed info
  3. Test locally before deploying
  4. Monitor logs for security events

Remember: When in doubt, reject the input! Better safe than sorry.