mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 12:16:36 +00:00
* telemetry: validate reports on the collect endpoint /api/collect is anonymous, so reports can't be authenticated, but a real master can't produce a non-UUID topology_id, a version outside N.NN(-enterprise), an unknown GOOS/GOARCH, or absurd counts — reject those to keep casual junk out of the collected data, and cap the request body at 4 KB. * telemetry: integration test fixtures pass collect validation The test's topology id and version were exactly the junk shapes the new validation rejects; use a UUID and a plain version number.
99 lines
3.3 KiB
Go
99 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/telemetry/proto"
|
|
"github.com/seaweedfs/seaweedfs/telemetry/server/storage"
|
|
protobuf "google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func validReport() *proto.TelemetryData {
|
|
return &proto.TelemetryData{
|
|
TopologyId: "38422678-6a0d-4482-aa33-65b90010ac47",
|
|
Version: "4.40",
|
|
Os: "linux/amd64",
|
|
VolumeServerCount: 5,
|
|
TotalDiskBytes: 123456789,
|
|
TotalVolumeCount: 42,
|
|
FilerCount: 2,
|
|
BrokerCount: 1,
|
|
}
|
|
}
|
|
|
|
func postCollect(t *testing.T, h *Handler, body []byte, contentType string) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
req := httptest.NewRequest(http.MethodPost, "/api/collect", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", contentType)
|
|
w := httptest.NewRecorder()
|
|
h.CollectTelemetry(w, req)
|
|
return w
|
|
}
|
|
|
|
func marshalReport(t *testing.T, data *proto.TelemetryData) []byte {
|
|
t.Helper()
|
|
body, err := protobuf.Marshal(&proto.TelemetryRequest{Data: data})
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
return body
|
|
}
|
|
|
|
func TestCollectTelemetryValidation(t *testing.T) {
|
|
// promauto registers on the global registry: one storage per test binary.
|
|
h := NewHandler(storage.NewPrometheusStorage())
|
|
|
|
t.Run("valid report accepted", func(t *testing.T) {
|
|
if w := postCollect(t, h, marshalReport(t, validReport()), "application/x-protobuf"); w.Code != http.StatusOK {
|
|
t.Errorf("got %d, want 200: %s", w.Code, w.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("enterprise version accepted", func(t *testing.T) {
|
|
data := validReport()
|
|
data.Version = "4.40-enterprise"
|
|
if w := postCollect(t, h, marshalReport(t, data), "application/x-protobuf"); w.Code != http.StatusOK {
|
|
t.Errorf("got %d, want 200: %s", w.Code, w.Body.String())
|
|
}
|
|
})
|
|
|
|
rejected := []struct {
|
|
name string
|
|
mutate func(*proto.TelemetryData)
|
|
}{
|
|
{"non-UUID topology_id", func(d *proto.TelemetryData) { d.TopologyId = "claude-diagnostic-probe" }},
|
|
{"empty topology_id", func(d *proto.TelemetryData) { d.TopologyId = "" }},
|
|
{"junk version", func(d *proto.TelemetryData) { d.Version = "probe" }},
|
|
{"version with suffix", func(d *proto.TelemetryData) { d.Version = "4.40-nightly" }},
|
|
{"junk os", func(d *proto.TelemetryData) { d.Os = "probe/probe" }},
|
|
{"os without slash", func(d *proto.TelemetryData) { d.Os = "linux" }},
|
|
{"negative count", func(d *proto.TelemetryData) { d.VolumeServerCount = -1 }},
|
|
{"absurd server count", func(d *proto.TelemetryData) { d.VolumeServerCount = 1_000_000 }},
|
|
{"absurd disk size", func(d *proto.TelemetryData) { d.TotalDiskBytes = 1 << 62 }},
|
|
}
|
|
for _, tc := range rejected {
|
|
t.Run(tc.name+" rejected", func(t *testing.T) {
|
|
data := validReport()
|
|
tc.mutate(data)
|
|
if w := postCollect(t, h, marshalReport(t, data), "application/x-protobuf"); w.Code != http.StatusBadRequest {
|
|
t.Errorf("got %d, want 400: %s", w.Code, w.Body.String())
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("wrong content type rejected", func(t *testing.T) {
|
|
if w := postCollect(t, h, marshalReport(t, validReport()), "application/json"); w.Code != http.StatusUnsupportedMediaType {
|
|
t.Errorf("got %d, want 415", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("oversized body rejected", func(t *testing.T) {
|
|
if w := postCollect(t, h, make([]byte, maxRequestBytes+1), "application/x-protobuf"); w.Code != http.StatusBadRequest {
|
|
t.Errorf("got %d, want 400", w.Code)
|
|
}
|
|
})
|
|
}
|