Skip to content

Commit 378d704

Browse files
authored
config: fix panic on nil value in headers name/value pair (#6425)
Found a bug during testing of v0.3.0 support with the Collector where the value passed in to the headers name/value pair list could be nil, causing a dereferencing error. This fixes that bug. --------- Signed-off-by: Alex Boten <[email protected]>
1 parent 1606707 commit 378d704

8 files changed

+191
-9
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
3030
- Removed the redundant handling of panic from the `HTML` function in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6373)
3131
- The `code.function` attribute emitted by `go.opentelemetry.io/contrib/bridges/otelslog` now stores just the function name instead the package path-qualified function name. The `code.namespace` attribute now stores the package path. (#6415)
3232
- The `code.function` attribute emitted by `go.opentelemetry.io/contrib/bridges/otelzap` now stores just the function name instead the package path-qualified function name. The `code.namespace` attribute now stores the package path. (#6423)
33+
- Return an error for `nil` values when unmarshaling `NameStringValuePair` in `go.opentelemetry.io/contrib/config`. (#6425)
3334

3435
<!-- Released section -->
3536
<!-- Don't change this section unless doing release -->

config/testdata/invalid_nil_name.json

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"file_format": "0.3",
3+
"disabled": false,
4+
"logger_provider": {
5+
"processors": [
6+
{
7+
"batch": {
8+
"schedule_delay": 5000,
9+
"export_timeout": 30000,
10+
"max_queue_size": 2048,
11+
"max_export_batch_size": 512,
12+
"exporter": {
13+
"otlp": {
14+
"protocol": "http/protobuf",
15+
"endpoint": "http://localhost:4318/v1/logs",
16+
"certificate": "/app/cert.pem",
17+
"client_key": "/app/cert.pem",
18+
"client_certificate": "/app/cert.pem",
19+
"headers": [
20+
{
21+
"name": "api-key",
22+
"value": "1234"
23+
},
24+
{
25+
"value": "nil-name"
26+
}
27+
],
28+
"headers_list": "api-key=1234",
29+
"compression": "gzip",
30+
"timeout": 10000,
31+
"insecure": false
32+
}
33+
}
34+
}
35+
},
36+
{
37+
"simple": {
38+
"exporter": {
39+
"console": {}
40+
}
41+
}
42+
}
43+
],
44+
"limits": {
45+
"attribute_value_length_limit": 4096,
46+
"attribute_count_limit": 128
47+
}
48+
}
49+
}

config/testdata/invalid_nil_name.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
file_format: "0.3"
2+
disabled: false
3+
logger_provider:
4+
processors:
5+
- batch:
6+
exporter:
7+
otlp:
8+
protocol: http/protobuf
9+
endpoint: http://localhost:4318/v1/logs
10+
headers:
11+
- name: api-key
12+
value: "1234"
13+
- value: nil-name
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"file_format": "0.3",
3+
"disabled": false,
4+
"logger_provider": {
5+
"processors": [
6+
{
7+
"batch": {
8+
"schedule_delay": 5000,
9+
"export_timeout": 30000,
10+
"max_queue_size": 2048,
11+
"max_export_batch_size": 512,
12+
"exporter": {
13+
"otlp": {
14+
"protocol": "http/protobuf",
15+
"endpoint": "http://localhost:4318/v1/logs",
16+
"certificate": "/app/cert.pem",
17+
"client_key": "/app/cert.pem",
18+
"client_certificate": "/app/cert.pem",
19+
"headers": [
20+
{
21+
"name": "api-key",
22+
"value": "1234"
23+
},
24+
{
25+
"name": "nil-value"
26+
}
27+
],
28+
"headers_list": "api-key=1234",
29+
"compression": "gzip",
30+
"timeout": 10000,
31+
"insecure": false
32+
}
33+
}
34+
}
35+
},
36+
{
37+
"simple": {
38+
"exporter": {
39+
"console": {}
40+
}
41+
}
42+
}
43+
],
44+
"limits": {
45+
"attribute_value_length_limit": 4096,
46+
"attribute_count_limit": 128
47+
}
48+
}
49+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
file_format: "0.3"
2+
disabled: false
3+
logger_provider:
4+
processors:
5+
- batch:
6+
exporter:
7+
otlp:
8+
protocol: http/protobuf
9+
endpoint: http://localhost:4318/v1/logs
10+
headers:
11+
- name: api-key
12+
value: "1234"
13+
- name: nil-value

config/v0.3.0/config_json.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,25 @@ func (j *NameStringValuePair) UnmarshalJSON(b []byte) error {
112112
if err := json.Unmarshal(b, &raw); err != nil {
113113
return err
114114
}
115-
if _, ok := raw["name"]; raw != nil && !ok {
116-
return errors.New("field name in NameStringValuePair: required")
115+
if _, ok := raw["name"]; !ok {
116+
return errors.New("json: cannot unmarshal field name in NameStringValuePair required")
117117
}
118-
if _, ok := raw["value"]; raw != nil && !ok {
119-
return errors.New("field value in NameStringValuePair: required")
118+
if _, ok := raw["value"]; !ok {
119+
return errors.New("json: cannot unmarshal field value in NameStringValuePair required")
120120
}
121-
type Plain NameStringValuePair
122-
var plain Plain
123-
if err := json.Unmarshal(b, &plain); err != nil {
124-
return err
121+
var name, value string
122+
var ok bool
123+
if name, ok = raw["name"].(string); !ok {
124+
return errors.New("yaml: cannot unmarshal field name in NameStringValuePair must be string")
125+
}
126+
if value, ok = raw["value"].(string); !ok {
127+
return errors.New("yaml: cannot unmarshal field value in NameStringValuePair must be string")
128+
}
129+
130+
*j = NameStringValuePair{
131+
Name: name,
132+
Value: &value,
125133
}
126-
*j = NameStringValuePair(plain)
127134
return nil
128135
}
129136

config/v0.3.0/config_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,16 @@ func TestParseYAML(t *testing.T) {
405405
wantErr: errors.New(`yaml: unmarshal errors:
406406
line 2: cannot unmarshal !!str ` + "`notabool`" + ` into bool`),
407407
},
408+
{
409+
name: "invalid nil name",
410+
input: "invalid_nil_name.yaml",
411+
wantErr: errors.New(`yaml: cannot unmarshal field name in NameStringValuePair required`),
412+
},
413+
{
414+
name: "invalid nil value",
415+
input: "invalid_nil_value.yaml",
416+
wantErr: errors.New(`yaml: cannot unmarshal field value in NameStringValuePair required`),
417+
},
408418
{
409419
name: "valid v0.2 config",
410420
input: "v0.2.yaml",
@@ -429,6 +439,7 @@ func TestParseYAML(t *testing.T) {
429439

430440
got, err := ParseYAML(b)
431441
if tt.wantErr != nil {
442+
require.Error(t, err)
432443
require.Equal(t, tt.wantErr.Error(), err.Error())
433444
} else {
434445
require.NoError(t, err)
@@ -459,6 +470,16 @@ func TestSerializeJSON(t *testing.T) {
459470
input: "invalid_bool.json",
460471
wantErr: errors.New(`json: cannot unmarshal string into Go struct field Plain.disabled of type bool`),
461472
},
473+
{
474+
name: "invalid nil name",
475+
input: "invalid_nil_name.json",
476+
wantErr: errors.New(`json: cannot unmarshal field name in NameStringValuePair required`),
477+
},
478+
{
479+
name: "invalid nil value",
480+
input: "invalid_nil_value.json",
481+
wantErr: errors.New(`json: cannot unmarshal field value in NameStringValuePair required`),
482+
},
462483
{
463484
name: "valid v0.2 config",
464485
input: "v0.2.json",
@@ -480,6 +501,7 @@ func TestSerializeJSON(t *testing.T) {
480501
err = json.Unmarshal(b, &got)
481502

482503
if tt.wantErr != nil {
504+
require.Error(t, err)
483505
require.Equal(t, tt.wantErr.Error(), err.Error())
484506
} else {
485507
require.NoError(t, err)

config/v0.3.0/config_yaml.go

+28
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package config // import "go.opentelemetry.io/contrib/config/v0.3.0"
55

66
import (
7+
"errors"
78
"fmt"
89
"reflect"
910
)
@@ -30,6 +31,33 @@ func (j *AttributeNameValueType) UnmarshalYAML(unmarshal func(interface{}) error
3031
return nil
3132
}
3233

34+
// UnmarshalYAML implements yaml.Unmarshaler.
35+
func (j *NameStringValuePair) UnmarshalYAML(unmarshal func(interface{}) error) error {
36+
var raw map[string]interface{}
37+
if err := unmarshal(&raw); err != nil {
38+
return err
39+
}
40+
if _, ok := raw["name"]; !ok {
41+
return errors.New("yaml: cannot unmarshal field name in NameStringValuePair required")
42+
}
43+
if _, ok := raw["value"]; !ok {
44+
return errors.New("yaml: cannot unmarshal field value in NameStringValuePair required")
45+
}
46+
var name, value string
47+
var ok bool
48+
if name, ok = raw["name"].(string); !ok {
49+
return errors.New("yaml: cannot unmarshal field name in NameStringValuePair must be string")
50+
}
51+
if value, ok = raw["value"].(string); !ok {
52+
return errors.New("yaml: cannot unmarshal field value in NameStringValuePair must be string")
53+
}
54+
*j = NameStringValuePair{
55+
Name: name,
56+
Value: &value,
57+
}
58+
return nil
59+
}
60+
3361
// UnmarshalYAML implements yaml.Unmarshaler.
3462
func (j *LanguageSpecificInstrumentation) UnmarshalYAML(unmarshal func(interface{}) error) error {
3563
var raw map[string]interface{}

0 commit comments

Comments
 (0)