diff --git a/model/textparse/protobufparse.go b/model/textparse/protobufparse.go index 637ae7b747..3795c28589 100644 --- a/model/textparse/protobufparse.go +++ b/model/textparse/protobufparse.go @@ -674,6 +674,10 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) { switch p.dec.GetType() { case dto.MetricType_SUMMARY: qq := p.dec.GetSummary().GetQuantile() + if p.fieldPos >= len(qq) { + p.fieldsDone = true + return false, "", "" + } q := qq[p.fieldPos] p.fieldsDone = p.fieldPos == len(qq)-1 return true, model.QuantileLabel, labels.FormatOpenMetricsFloat(q.GetQuantile()) diff --git a/model/textparse/protobufparse_test.go b/model/textparse/protobufparse_test.go index 3a4f4abdda..12e4b959a7 100644 --- a/model/textparse/protobufparse_test.go +++ b/model/textparse/protobufparse_test.go @@ -5911,6 +5911,32 @@ func generateValidMetricName(r *rand.Rand) string { return generateString(r, validFirstRunes, validMetricNameRunes) } +// TestProtobufParseSummaryNoQuantilesNoPanic is a regression test for a panic +// when Next() is called without Series() on a summary with no quantiles. +func TestProtobufParseSummaryNoQuantilesNoPanic(t *testing.T) { + buf := metricFamiliesToProtobuf(t, []string{` +name: "no_quantile_summary" +help: "A summary with no quantile entries." +type: SUMMARY +metric: < + summary: < + sample_count: 10 + sample_sum: 1.5 + > +> +`}) + + p := NewProtobufParser(buf.Bytes(), false, false, false, false, labels.NewSymbolTable()) + require.NotPanics(t, func() { + for { + _, err := p.Next() + if errors.Is(err, io.EOF) || err != nil { + break + } + } + }) +} + func generateString(r *rand.Rand, firstRunes, restRunes []rune) string { result := make([]rune, 1+r.Intn(20)) for i := range result {