If you discover a security vulnerability in this project, please report it by creating a private security advisory on GitHub or by emailing the maintainers directly. Please do not create public issues for security vulnerabilities.
Please include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
We will respond to security reports within 48 hours and provide a fix within 7 days for critical vulnerabilities.
- Implementation: Uses
crypto/randinstead ofmath/rand - Impact: Prevents predictability in user agent selection
- Location:
useragent.go:256-267(secureRandomInt function)
func secureRandomInt(max int) (int, error) {
if max <= 0 {
return 0, errors.New("max must be positive")
}
nBig, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
if err != nil {
return 0, err
}
return int(nBig.Int64()), nil
}- Implementation: All Manager operations protected with RWMutex
- Impact: Prevents race conditions in concurrent access
- Location:
useragent.go:39-45(Manager struct)
type Manager struct {
mu sync.RWMutex
desktopAgents []UserAgent
mobileAgents []UserAgent
config Config
}- Implementation: Validates all user agent data on load
- Checks:
- User agent string not empty
- String length between 10 and 1000 characters
- Percentage between 0 and 100
- Agent type is valid (desktop, mobile, random)
- Location:
useragent.go:145-158(validateAgent function)
- Implementation: Returns copies of internal data, not references
- Impact: Prevents external modification of internal state
- Location:
useragent.go:160-180(GetAllDesktop, GetAllMobile)
All database queries use parameterized statements:
// SECURE - Parameterized query
query := `INSERT INTO request_logs (user_agent, agent_type, ...) VALUES (?, ?, ?)`
result, err := db.conn.ExecContext(ctx, query, log.UserAgent, log.AgentType, ...)
// INSECURE - Don't do this
query := fmt.Sprintf("INSERT INTO request_logs VALUES ('%s', '%s')", ua, type)Location: internal/database/database.go:85-103
Database Inputs:
- User agent max length: 1000 characters
- IP address max length: 45 characters (IPv6)
- Endpoint max length: 255 characters
- Agent type: enum validation (desktop, mobile, random)
Location: internal/database/database.go:338-368
API Inputs:
- Limit parameter: 1-1000 range validation
- IP address: validated with
net.ParseIP - Error messages: HTML escaped and sanitized
Location: internal/api/handlers.go:340-378
Error messages are sanitized to prevent information leakage:
func sanitizeErrorMessage(message string) string {
message = html.EscapeString(message)
sensitivePatterns := []string{
"/home/", "/usr/", "/var/",
"password", "token", "secret", "key",
}
for _, pattern := range sensitivePatterns {
if strings.Contains(strings.ToLower(message), pattern) {
return "an error occurred"
}
}
if len(message) > 200 {
return "an error occurred"
}
return message
}Location: internal/api/handlers.go:355-378
Prevents abuse with configurable per-IP rate limiting:
func RateLimitMiddleware(maxRequests int, window time.Duration)Configuration: MAX_REQUESTS_PER_MINUTE environment variable
Location: internal/api/handlers.go:380-402
All responses include security headers:
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Content-Security-Policy", "default-src 'self'; ...")Location: internal/web/server.go:27-30, internal/api/handlers.go:246-248
Safely extracts client IP with validation:
func getClientIP(r *http.Request) string {
// Validates X-Forwarded-For
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
ips := strings.Split(xff, ",")
if len(ips) > 0 {
ip := strings.TrimSpace(ips[0])
if net.ParseIP(ip) != nil { // Validation!
return ip
}
}
}
// Falls back to RemoteAddr
...
}Location: internal/api/handlers.go:322-343
Container runs as unprivileged user appuser:
USER appuserread_only: truesecurity_opt:
- no-new-privileges:truedeploy:
resources:
limits:
cpus: '0.5'
memory: 256MUses Alpine Linux for minimal attack surface.
Deploy behind a reverse proxy (nginx, Caddy, Traefik) with TLS:
server {
listen 443 ssl http2;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}# Production settings
export APP_ENV=production
export LOG_LEVEL=warn
export MAX_REQUESTS_PER_MINUTE=50
# Secure database
chmod 600 /data/useragent.db# Scan image for vulnerabilities
docker scan commonuseragent:latest
# Run Trivy scan
trivy image commonuseragent:latest- Enable application logging
- Monitor rate limit hits
- Set up alerts for unusual patterns
- Regularly review database logs
# Check for updates
go list -m -u all
# Update dependencies
go get -u ./...
go mod tidyBefore deploying to production:
- Use HTTPS/TLS
- Set
APP_ENV=production - Configure appropriate rate limits
- Set strong file permissions on database
- Run security scanner:
make security-scan - Run tests with race detector:
make test-race - Review and set resource limits
- Enable monitoring and logging
- Use non-root user in Docker
- Scan Docker image for vulnerabilities
- Set up backup for database
- Configure firewall rules
- Review and minimize exposed ports
✅ SQL Injection - Parameterized queries ✅ XSS - HTML escaping, CSP headers ✅ Information Leakage - Error sanitization ✅ DoS - Rate limiting, resource limits ✅ CSRF - Same-origin policy ✅ Predictable Random - Crypto-secure RNG ✅ Race Conditions - Thread-safe operations ✅ Container Escape - Non-root user, security options
- Mitigation: Use infrastructure-level DDoS protection (CloudFlare, AWS Shield)
- Mitigation: Encrypt backups, secure backup storage
- Mitigation: Sanitize before logging, use structured logging
# Full security test suite
make security-scan
# Race condition detection
make test-race
# Static analysis
make staticcheck
# All checks
make check- Test SQL Injection:
curl "http://localhost:8080/api/logs?limit='; DROP TABLE request_logs; --"
# Should be safely handled- Test Rate Limiting:
for i in {1..150}; do curl http://localhost:8080/api/random; done
# Should get 429 after limit- Test Error Handling:
curl http://localhost:8080/api/logs?limit=abc
# Should return sanitized errorThis application follows:
- OWASP Top 10 security guidelines
- CWE/SANS Top 25 mitigation strategies
- Docker security best practices
- Go secure coding guidelines
Security updates will be released as patch versions (e.g., v2.0.1) and communicated via:
- GitHub Security Advisories
- Release notes
- README changelog
For security concerns, contact the maintainers through GitHub's private security advisory feature.