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

271 lines
6.4 KiB
Markdown

# Input Validation Quick Reference
## For Developers: How to Validate User Input
### Basic Usage
```go
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
```go
// Protect PDF endpoint
protectedHandler := middleware.MaxRequestSize(1024 * 1024)(
http.HandlerFunc(handler.ExportPDF),
)
mux.Handle("/export/pdf", protectedHandler)
```
### Global Application (Already Applied)
```go
// 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
```go
lang, err := validator.ValidateLanguage(r.URL.Query().Get("lang"))
if err != nil {
HandleError(w, r, BadRequestError("Invalid language. Supported: en, es"))
return
}
```
### File Upload
```go
// 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
```go
// 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
```bash
# 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
```go
// 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
```go
// 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
```go
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**
```go
// ❌ 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**
```go
// ❌ 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.