diff --git a/storage/remote/read.go b/storage/remote/read.go index 37587e5bfa..706d632c2a 100644 --- a/storage/remote/read.go +++ b/storage/remote/read.go @@ -15,6 +15,7 @@ package remote import ( "context" + "fmt" "sort" "github.com/prometheus/common/model" @@ -77,6 +78,10 @@ func (q *querier) Select(matchers ...*labels.Matcher) storage.SeriesSet { for _, ts := range res { labels := labelPairsToLabels(ts.Labels) removeLabels(&labels, added) + if err := validateLabelsAndMetricName(labels); err != nil { + return errSeriesSet{err: err} + } + series = append(series, &concreteSeries{ labels: labels, samples: ts.Samples, @@ -205,6 +210,22 @@ func (c *concreteSeriesIterator) Err() error { return nil } +// validateLabelsAndMetricName validates the label names/values and metric names returned from remote read. +func validateLabelsAndMetricName(ls labels.Labels) error { + for _, l := range ls { + if l.Name == labels.MetricName && !model.IsValidMetricName(model.LabelValue(l.Value)) { + return fmt.Errorf("Invalid metric name: %v", l.Value) + } + if !model.LabelName(l.Name).IsValid() { + return fmt.Errorf("Invalid label name: %v", l.Name) + } + if !model.LabelValue(l.Value).IsValid() { + return fmt.Errorf("Invalid label value: %v", l.Value) + } + } + return nil +} + // addExternalLabels adds matchers for each external label. External labels // that already have a corresponding user-supplied matcher are skipped, as we // assume that the user explicitly wants to select a different value for them. diff --git a/storage/remote/read_test.go b/storage/remote/read_test.go index c32ee9f672..376dce3a3b 100644 --- a/storage/remote/read_test.go +++ b/storage/remote/read_test.go @@ -27,6 +27,128 @@ import ( "github.com/prometheus/prometheus/storage" ) +func TestValidateLabelsAndMetricName(t *testing.T) { + tests := []struct { + result model.Matrix + expectedErr string + shouldPass bool + }{ + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "labelName": "labelValue", + }, + }, + }, + expectedErr: "", + shouldPass: true, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "_labelName": "labelValue", + }, + }, + }, + expectedErr: "", + shouldPass: true, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "@labelName": "labelValue", + }, + }, + }, + expectedErr: "Invalid label name: @labelName", + shouldPass: false, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "123labelName": "labelValue", + }, + }, + }, + expectedErr: "Invalid label name: 123labelName", + shouldPass: false, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "": "labelValue", + }, + }, + }, + expectedErr: "Invalid label name: ", + shouldPass: false, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "name", + "labelName": model.LabelValue([]byte{0xff}), + }, + }, + }, + expectedErr: "Invalid label value: " + string([]byte{0xff}), + shouldPass: false, + }, + { + result: model.Matrix{ + &model.SampleStream{ + Metric: model.Metric{ + "__name__": "@invalid_name", + }, + }, + }, + expectedErr: "Invalid metric name: @invalid_name", + shouldPass: false, + }, + } + + for _, test := range tests { + var err error + for _, ss := range test.result { + ls := make(labels.Labels, 0, len(ss.Metric)) + for k, v := range ss.Metric { + ls = append(ls, labels.Label{ + Name: string(k), + Value: string(v), + }) + } + err = validateLabelsAndMetricName(ls) + if err != nil { + break + } + } + if test.shouldPass { + if err != nil { + t.Fatalf("Test should pass, got unexpected error: %v", err) + } + continue + } + if err != nil { + if err.Error() != test.expectedErr { + t.Fatalf("Unexpected error, got: %v, expected: %v", err, test.expectedErr) + } + } else { + t.Fatalf("Expected error, got none") + } + } +} + func mustNewLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher { m, err := labels.NewMatcher(mt, name, val) if err != nil {