Repository: prometheus/common
Pull request #207 (2019-09-04 10:31:20)

Check for various number formats we don't support.
Go 1.13 adds support for parsing a number of different number
formats, which could lead to inadventanly expanding what is
considered valid Prometheus text format. Test for this and
add safety checks for future potential changes.

All tests passed on Go 1.12, before code changes.

@beorn7 Presuming this looks good, I'll handle the other uses next.

brian-brazil: Yes, I wanted to do this first as it's the "official" parser.

Patch:
diff --git a/expfmt/text_parse.go b/expfmt/text_parse.go
index ec3d86b..be1ae2f 100644
--- a/expfmt/text_parse.go
+++ b/expfmt/text_parse.go
@@ -325,7 +325,7 @@ func (p *TextParser) startLabelValue() stateFn {
 	// - Other labels have to be added to currentLabels for signature calculation.
 	if p.currentMF.GetType() == dto.MetricType_SUMMARY {
 		if p.currentLabelPair.GetName() == model.QuantileLabel {
-			if p.currentQuantile, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
+			if p.currentQuantile, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
 				// Create a more helpful error message.
 				p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
 				return nil
@@ -337,7 +337,7 @@ func (p *TextParser) startLabelValue() stateFn {
 	// Similar special treatment of histograms.
 	if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
 		if p.currentLabelPair.GetName() == model.BucketLabel {
-			if p.currentBucket, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
+			if p.currentBucket, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
 				// Create a more helpful error message.
 				p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
 				return nil
@@ -392,7 +392,7 @@ func (p *TextParser) readingValue() stateFn {
 	if p.readTokenUntilWhitespace(); p.err != nil {
 		return nil // Unexpected end of input.
 	}
-	value, err := strconv.ParseFloat(p.currentToken.String(), 64)
+	value, err := parseFloat(p.currentToken.String())
 	if err != nil {
 		// Create a more helpful error message.
 		p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
@@ -755,3 +755,10 @@ func histogramMetricName(name string) string {
 		return name
 	}
 }
+
+func parseFloat(s string) (float64, error) {
+	if strings.ContainsAny(s, "p_") {
+		return 0, fmt.Errorf("Unsupported character in float")
+	}
+	return strconv.ParseFloat(s, 64)
+}


Test patch:
diff --git a/expfmt/text_create_test.go b/expfmt/text_create_test.go
index 86e767c..014351d 100644
--- a/expfmt/text_create_test.go
+++ b/expfmt/text_create_test.go
@@ -109,7 +109,7 @@ name{labelname="val2",basename="basevalue"} 0.23 1234567890
 							},
 						},
 						Gauge: &dto.Gauge{
-							Value: proto.Float64(3.14E42),
+							Value: proto.Float64(3.14e42),
 						},
 					},
 				},
diff --git a/expfmt/text_parse_test.go b/expfmt/text_parse_test.go
index 76c9511..c3159b6 100644
--- a/expfmt/text_parse_test.go
+++ b/expfmt/text_parse_test.go
@@ -564,6 +564,52 @@ metric_bucket{le="bla"} 3.14
 			in:  "metric{l=\"\xbd\"} 3.14\n",
 			err: "text format parsing error in line 1: invalid label value \"\\xbd\"",
 		},
+		// 20: Go 1.13 sometimes allows underscores in numbers.
+		{
+			in:  "foo 1_2\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 21: Go 1.13 supports hex floating point.
+		{
+			in:  "foo 0x1p-3\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 22: Check for various other literals variants, just in case.
+		{
+			in:  "foo 0b1\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 23:
+		{
+			in:  "foo 0o1\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 24:
+		{
+			in:  "foo 0x1\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 25:
+		{
+			in:  "foo 0x1\n",
+			err: "text format parsing error in line 1: expected float as value",
+		},
+		// 26: Check histogram label.
+		{
+			in: `
+# TYPE metric histogram
+metric_bucket{le="0x1p-3"} 3.14
+`,
+			err: "text format parsing error in line 3: expected float as value for 'le' label",
+		},
+		// 27: Check quantile label.
+		{
+			in: `
+# TYPE metric summary
+metric{quantile="0x1p-3"} 3.14
+`,
+			err: "text format parsing error in line 3: expected float as value for 'quantile' label",
+		},
 	}
 
 	for i, scenario := range scenarios {