1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-02-09 13:37:12 +02:00

baggage: Fix Parse to validate member value before percent-decoding (#4755)

This commit is contained in:
Robert Pająk 2023-12-19 14:38:58 +01:00 committed by GitHub
parent 15f0ab964c
commit 43bd47de6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 17 deletions

View File

@ -26,6 +26,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Improve `go.opentelemetry.io/otel/propagation.TraceContext`'s performance. (#4721) - Improve `go.opentelemetry.io/otel/propagation.TraceContext`'s performance. (#4721)
- Improve `go.opentelemetry.io/otel/baggage` performance. (#4743) - Improve `go.opentelemetry.io/otel/baggage` performance. (#4743)
### Fixed
- Fix `Parse` in `go.opentelemetry.io/otel/baggage` to validate member value before percent-decoding. (#4755)
## [1.21.0/0.44.0] 2023-11-16 ## [1.21.0/0.44.0] 2023-11-16
### Removed ### Removed

View File

@ -254,11 +254,7 @@ func parseMember(member string) (Member, error) {
return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n) return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
} }
var ( var props properties
key, value string
props properties
)
keyValue, properties, found := strings.Cut(member, propertyDelimiter) keyValue, properties, found := strings.Cut(member, propertyDelimiter)
if found { if found {
// Parse the member properties. // Parse the member properties.
@ -279,19 +275,19 @@ func parseMember(member string) (Member, error) {
} }
// "Leading and trailing whitespaces are allowed but MUST be trimmed // "Leading and trailing whitespaces are allowed but MUST be trimmed
// when converting the header into a data structure." // when converting the header into a data structure."
key = strings.TrimSpace(k) key := strings.TrimSpace(k)
var err error
value, err = url.PathUnescape(strings.TrimSpace(v))
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
}
if !validateKey(key) { if !validateKey(key) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key) return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
} }
if !validateValue(value) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
val := strings.TrimSpace(v)
if !validateValue(val) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
}
value, err := url.PathUnescape(val)
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %v", errInvalidValue, err)
}
return Member{key: key, value: value, properties: props, hasData: true}, nil return Member{key: key, value: value, properties: props, hasData: true}, nil
} }

View File

@ -384,9 +384,9 @@ func TestBaggageParse(t *testing.T) {
}, },
{ {
name: "url encoded value", name: "url encoded value",
in: "key1=val%252", in: "key1=val%252%2C",
want: baggage.List{ want: baggage.List{
"key1": {Value: "val%2"}, "key1": {Value: "val%2,"},
}, },
}, },
{ {
@ -414,6 +414,11 @@ func TestBaggageParse(t *testing.T) {
in: "foo=\\", in: "foo=\\",
err: errInvalidValue, err: errInvalidValue,
}, },
{
name: "invalid member: improper url encoded value",
in: "key1=val%",
err: errInvalidValue,
},
{ {
name: "invalid property: no key", name: "invalid property: no key",
in: "foo=1;=v", in: "foo=1;=v",

View File

@ -47,7 +47,7 @@ func (m member) Member(t *testing.T) baggage.Member {
} }
props = append(props, p) props = append(props, p)
} }
bMember, err := baggage.NewMember(m.Key, url.QueryEscape(m.Value), props...) bMember, err := baggage.NewMember(m.Key, url.PathEscape(m.Value), props...)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -248,6 +248,55 @@ func TestInjectBaggageToHTTPReq(t *testing.T) {
} }
} }
func TestBaggageInjectExtractRoundtrip(t *testing.T) {
propagator := propagation.Baggage{}
tests := []struct {
name string
mems members
}{
{
name: "two simple values",
mems: members{
{Key: "key1", Value: "val1"},
{Key: "key2", Value: "val2"},
},
},
{
name: "values with escaped chars",
mems: members{
{Key: "key1", Value: "val3=4"},
{Key: "key2", Value: "mess,me%up"},
},
},
{
name: "with properties",
mems: members{
{Key: "key1", Value: "val1"},
{
Key: "key2",
Value: "val2",
Properties: []property{
{Key: "prop", Value: "1"},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := tt.mems.Baggage(t)
req, _ := http.NewRequest("GET", "http://example.com", nil)
ctx := baggage.ContextWithBaggage(context.Background(), b)
propagator.Inject(ctx, propagation.HeaderCarrier(req.Header))
ctx = propagator.Extract(context.Background(), propagation.HeaderCarrier(req.Header))
got := baggage.FromContext(ctx)
assert.Equal(t, b, got)
})
}
}
func TestBaggagePropagatorGetAllKeys(t *testing.T) { func TestBaggagePropagatorGetAllKeys(t *testing.T) {
var propagator propagation.Baggage var propagator propagation.Baggage
want := []string{"baggage"} want := []string{"baggage"}