Skip to content

Commit b6199a2

Browse files
committed
[internal/splunk] Splunk metric names should be validated via regex
1 parent f9cd72a commit b6199a2

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

internal/splunk/common.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package splunk // import "github.com/open-telemetry/opentelemetry-collector-cont
55

66
import (
77
"encoding/json"
8+
"regexp"
89
"strconv"
910
"strings"
1011
)
@@ -31,6 +32,14 @@ const (
3132
DefaultRawPath = "/services/collector/raw"
3233
DefaultHealthPath = "/services/collector/health"
3334
DefaultAckPath = "/services/collector/ack"
35+
36+
// https://docs.splunk.com/Documentation/Splunk/9.2.1/Metrics/Overview#What_is_a_metric_data_point.3F
37+
// metric name can contain letters, numbers, underscore, dot or colon. cannot start with number or underscore, or contain metric_name
38+
metricNamePattern = "^metric_name:([A-Za-z\\.:][A-Za-z0-9_\\.:]*)$"
39+
)
40+
41+
var (
42+
metricNameRegexp = regexp.MustCompile(metricNamePattern)
3443
)
3544

3645
// AccessTokenPassthroughConfig configures passing through access tokens.
@@ -55,6 +64,15 @@ func (e *Event) IsMetric() bool {
5564
return e.Event == HecEventMetricType || (e.Event == nil && len(e.GetMetricValues()) > 0)
5665
}
5766

67+
// checks if the field name matches the requirements for a metric datapoint field,
68+
// and returns the metric name and a bool indicating whether the field is a metric.
69+
func getMetricNameFromField(fieldName string) (string, bool) {
70+
if matches := metricNameRegexp.FindStringSubmatch(fieldName); len(matches) > 1 {
71+
return matches[1], !strings.Contains(matches[1], "metric_name")
72+
}
73+
return "", false
74+
}
75+
5876
// GetMetricValues extracts metric key value pairs from a Splunk HEC metric.
5977
func (e *Event) GetMetricValues() map[string]any {
6078
if v, ok := e.Fields["metric_name"]; ok {
@@ -63,8 +81,11 @@ func (e *Event) GetMetricValues() map[string]any {
6381

6482
values := map[string]any{}
6583
for k, v := range e.Fields {
66-
if strings.HasPrefix(k, "metric_name:") {
67-
values[k[12:]] = v
84+
// only consider metric name if it fits regex criteria.
85+
// use matches[1] since first element contains entire string.
86+
// first subgroup will be the actual metric name.
87+
if metricName, ok := getMetricNameFromField(k); ok {
88+
values[metricName] = v
6889
}
6990
}
7091
return values

internal/splunk/common_test.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,21 @@ func TestGetValues(t *testing.T) {
1616
Fields: map[string]any{},
1717
}
1818
assert.Equal(t, map[string]any{}, metric.GetMetricValues())
19+
metric.Fields["metric_name:x"] = "y"
20+
assert.Equal(t, map[string]any{"x": "y"}, metric.GetMetricValues())
1921
metric.Fields["metric_name:foo"] = "bar"
20-
assert.Equal(t, map[string]any{"foo": "bar"}, metric.GetMetricValues())
22+
assert.Equal(t, map[string]any{"x": "y", "foo": "bar"}, metric.GetMetricValues())
2123
metric.Fields["metric_name:foo2"] = "foobar"
22-
assert.Equal(t, map[string]any{"foo": "bar", "foo2": "foobar"}, metric.GetMetricValues())
24+
assert.Equal(t, map[string]any{"x": "y", "foo": "bar", "foo2": "foobar"}, metric.GetMetricValues())
25+
metric.Fields["metric_name:foo:123_456.Bar"] = "quux"
26+
assert.Equal(t, map[string]any{"x": "y", "foo": "bar", "foo2": "foobar", "foo:123_456.Bar": "quux"}, metric.GetMetricValues())
27+
// fields that aren't allowed
28+
metric.Fields["metric_name:foo bar"] = "baz" // contains space
29+
metric.Fields["metric_name:foo?"] = "baz" // illegal character
30+
metric.Fields["metric_name:1stfoo"] = "baz" // starts with number
31+
metric.Fields["metric_name:_foo"] = "baz" // starts with underscore
32+
metric.Fields["metric_name:foo_metric_name:bar"] = "baz" // name contains "metric_name"
33+
assert.Equal(t, map[string]any{"x": "y", "foo": "bar", "foo2": "foobar", "foo:123_456.Bar": "quux"}, metric.GetMetricValues())
2334
}
2435

2536
func TestSingleValue(t *testing.T) {

0 commit comments

Comments
 (0)