Skip to content

Commit

Permalink
fix: make content negotiation more resilient to bad input
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Nov 7, 2024
1 parent a74067b commit b2fd70c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
35 changes: 22 additions & 13 deletions negotiation/negotiation.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,33 +59,38 @@ func SelectQValueFast(header string, allowed []string) string {
bestQ := 0.0

name := ""
start := -1
start := 0
end := 0

for pos, char := range header {
// Format is like "a; q=0.5, b;q=1.0,c; q=0.3"
if char == ';' {
name = header[start : end+1]
start = -1
start = pos + 1
end = start
continue
}

if char == ',' || pos == len(header)-1 {
q := 1.0
if pos == len(header)-1 {
// This is the end, so it must be the name.
// Example: "application/yaml"
name = header[start : pos+1]
} else if name == "" {
// No name yet means we did not encounter a `;`.
if char != ',' && char != ' ' && char != '\t' {
// Update the end if it's not a comma or whitespace (i.e. end of string).
end = pos
}
if name == "" {
// No name yet means we did not encounter a `;`. Either this is a `,`
// or the end of the string so whatever we have is the name.
// Example: "a, b, c"
name = header[start:pos]
name = header[start : end+1]
} else {
if parsed, _ := strconv.ParseFloat(header[start+2:end+1], 64); parsed > 0 {
q = parsed
if len(header) > end+1 {
if parsed, _ := strconv.ParseFloat(header[start+2:end+1], 64); parsed > 0 {
q = parsed
}
}
}
start = -1
start = pos + 1
end = start

found := false
for _, n := range allowed {
Expand All @@ -97,19 +102,23 @@ func SelectQValueFast(header string, allowed []string) string {

if !found {
// Skip formats we don't support.
name = ""
continue
}

if q > bestQ || (q == bestQ && name == allowed[0]) {
bestQ = q
best = name
}
name = ""
continue
}

if char != ' ' && char != '\t' {
// Only advance end if it's not whitespace.
end = pos
if start == -1 {
if header[start] == ' ' || header[start] == '\t' {
// Trim leading whitespace.
start = pos
}
}
Expand Down
5 changes: 5 additions & 0 deletions negotiation/negotiation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func TestNoMatchFast(t *testing.T) {
assert.Equal(t, "", SelectQValueFast("a; q=1.0, b;q=1.0,c; q=0.3", []string{"d", "e"}))
}

func TestMalformedFast(t *testing.T) {
assert.Equal(t, "", SelectQValueFast("a;,", []string{"d", "e"}))
assert.Equal(t, "a", SelectQValueFast(",a ", []string{"a", "b"}))
}

var BenchResult string

func BenchmarkMatch(b *testing.B) {
Expand Down

0 comments on commit b2fd70c

Please sign in to comment.