From 85a12dc4bb4b0cf7f9118a668dde90b1e7da2651 Mon Sep 17 00:00:00 2001 From: George MacRorie Date: Mon, 6 Jan 2020 10:33:22 +0000 Subject: [PATCH] fix(models): filter out reserved tag keys in points parser --- models/points.go | 31 +++++++++++++++++++++++++++++-- models/points_test.go | 34 +++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/models/points.go b/models/points.go index c978b3157ec..d66628afb3e 100644 --- a/models/points.go +++ b/models/points.go @@ -18,16 +18,31 @@ import ( "github.com/influxdata/influxdb/pkg/escape" ) -// Values used to store the field key and measurement name as special internal tags. const ( + // Values used to store the field key and measurement name as special internal tags. FieldKeyTagKey = "\xff" MeasurementTagKey = "\x00" + + // reserved tag keys which when present cause the point to be discarded + // and an error returned + reservedFieldTagKey = "_field" + reservedMeasurementTagKey = "_measurement" + reservedTimeTagKey = "time" ) -// Predefined byte representations of special tag keys. var ( + // Predefined byte representations of special tag keys. FieldKeyTagKeyBytes = []byte(FieldKeyTagKey) MeasurementTagKeyBytes = []byte(MeasurementTagKey) + + // set of reserved tag keys which cannot be present when a point is being parsed. + reservedTagKeys = [][]byte{ + FieldKeyTagKeyBytes, + MeasurementTagKeyBytes, + []byte(reservedFieldTagKey), + []byte(reservedMeasurementTagKey), + []byte(reservedTimeTagKey), + } ) type escapeSet struct { @@ -584,6 +599,18 @@ func scanKey(buf []byte, i int) (int, []byte, error) { } } + // Iterate over tags keys ensure that we do not encounter any + // of the reserved tag keys such as _measurement or _field. + for j := 0; j < commas; j++ { + _, key := scanTo(buf[indices[j]:indices[j+1]-1], 0, '=') + + for _, reserved := range reservedTagKeys { + if bytes.Equal(key, reserved) { + return i, buf[start:i], fmt.Errorf("cannot use reserved tag key %q", key) + } + } + } + // Now we know where the key region is within buf, and the location of tags, we // need to determine if duplicate tags exist and if the tags are sorted. This iterates // over the list comparing each tag in the sequence with each other. diff --git a/models/points_test.go b/models/points_test.go index 154b8055ed3..eca4d0d7d5a 100644 --- a/models/points_test.go +++ b/models/points_test.go @@ -2,6 +2,7 @@ package models_test import ( "bytes" + "errors" "fmt" "io" "math" @@ -102,24 +103,35 @@ func TestPoint_Tags(t *testing.T) { examples := []struct { Point string Tags models.Tags + Err error }{ - {`cpu value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value"})}, - {"cpu,tag0=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0"})}, - {"cpu,tag0=v0,tag1=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0", "tag1": "v0"})}, - {`cpu,tag0=v\ 0 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0"})}, - {`cpu,tag0=v\ 0\ 1,tag1=v2 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0 1", "tag1": "v2"})}, - {`cpu,tag0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ","})}, - {`cpu,ta\ g0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "ta g0": ","})}, - {`cpu,tag0=\,1 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ",1"})}, - {`cpu,tag0=1\"\",t=k value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": `1\"\"`, "t": "k"})}, + {`cpu value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value"}), nil}, + {"cpu,tag0=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0"}), nil}, + {"cpu,tag0=v0,tag1=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0", "tag1": "v0"}), nil}, + {`cpu,tag0=v\ 0 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0"}), nil}, + {`cpu,tag0=v\ 0\ 1,tag1=v2 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0 1", "tag1": "v2"}), nil}, + {`cpu,tag0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ","}), nil}, + {`cpu,ta\ g0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "ta g0": ","}), nil}, + {`cpu,tag0=\,1 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ",1"}), nil}, + {`cpu,tag0=1\"\",t=k value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": `1\"\"`, "t": "k"}), nil}, + {"cpu,_measurement=v0,tag0=v0 value=1", nil, errors.New(`unable to parse 'cpu,_measurement=v0,tag0=v0 value=1': cannot use reserved tag key "_measurement"`)}, + // the following are all unsorted tag keys to ensure this works for both cases + {"cpu,tag0=v0,_measurement=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,_measurement=v0 value=1': cannot use reserved tag key "_measurement"`)}, + {"cpu,tag0=v0,_field=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,_field=v0 value=1': cannot use reserved tag key "_field"`)}, + {"cpu,tag0=v0,time=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,time=v0 value=1': cannot use reserved tag key "time"`)}, } for _, example := range examples { t.Run(example.Point, func(t *testing.T) { pts, err := models.ParsePointsString(example.Point, "mm") if err != nil { - t.Fatal(err) - } else if len(pts) != 1 { + if !reflect.DeepEqual(example.Err, err) { + t.Fatalf("expected %#v, found %#v", example.Err, err) + } + return + } + + if len(pts) != 1 { t.Fatalf("parsed %d points, expected 1", len(pts)) }