diff --git a/main.go b/main.go index a9fc26f..d6d22f6 100644 --- a/main.go +++ b/main.go @@ -103,6 +103,26 @@ func forwardToPrometheus(metrics []byte, forwardURL string) error { return nil } +// maxTelemetryBodySize is the maximum accepted size for a telemetry POST body. +const maxTelemetryBodySize = 10 * 1024 * 1024 // 10 MB + +// isValidClusterID accepts only alphanumeric characters, hyphens, underscores, +// and dots (standard Kubernetes naming). This prevents Prometheus text-format +// injection via the X-Cluster-ID header (e.g. a value containing '"' or '\n' +// would corrupt the label output forwarded to VictoriaMetrics). +func isValidClusterID(s string) bool { + if s == "" || len(s) > 253 { + return false + } + for _, c := range s { + if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.') { + return false + } + } + return true +} + func handleTelemetry(w http.ResponseWriter, r *http.Request, forwardURL string) { startTime := time.Now() @@ -113,12 +133,13 @@ func handleTelemetry(w http.ResponseWriter, r *http.Request, forwardURL string) } clusterID := r.Header.Get("X-Cluster-ID") - if clusterID == "" { - log.Printf("Request rejected: missing X-Cluster-ID header") - http.Error(w, "X-Cluster-ID header is required", http.StatusBadRequest) + if !isValidClusterID(clusterID) { + log.Printf("Request rejected: invalid or missing X-Cluster-ID header") + http.Error(w, "X-Cluster-ID header is required and must contain only alphanumeric characters, hyphens, underscores, or dots", http.StatusBadRequest) return } + r.Body = http.MaxBytesReader(w, r.Body, maxTelemetryBodySize) body, err := io.ReadAll(r.Body) if err != nil { log.Printf("Error reading request body: %v", err)