internal: fix Unicode zero character handling (#841)
A fix was submitted to our dependency, tests added to validate the problematic scenario. We also added some fuzz tests to check for additional issues, none were found.
This commit is contained in:
parent
4cdbaaf271
commit
724dd47aa3
3 changed files with 114 additions and 3 deletions
2
go.mod
2
go.mod
|
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/jsonschema-go v0.4.2
|
||||
github.com/segmentio/encoding v0.5.3
|
||||
github.com/segmentio/encoding v0.5.4
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2
|
||||
golang.org/x/oauth2 v0.35.0
|
||||
golang.org/x/tools v0.42.0
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -6,8 +6,8 @@ github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbc
|
|||
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
|
||||
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
|
||||
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
|
@ -61,3 +64,111 @@ func TestUnmarshalCaseSensitivity(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalNullCharacter(t *testing.T) {
|
||||
type Target struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
json string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "null char in middle",
|
||||
json: `{"fi\u0000eld": "value"}`,
|
||||
},
|
||||
{
|
||||
name: "null char at end",
|
||||
json: `{"field\u0000": "value"}`,
|
||||
},
|
||||
{
|
||||
name: "null char at start",
|
||||
json: `{"\u0000field": "value"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got Target
|
||||
if err := Unmarshal([]byte(tt.json), &got); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
if got.Field != "" {
|
||||
t.Errorf("Field was set: %s", tt.json)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzUnmarshalKeyComparison(f *testing.F) {
|
||||
// Add seed corpus for expected valid key and the known problematic cases.
|
||||
f.Add("field")
|
||||
f.Add("FIELD")
|
||||
f.Add("field\x00")
|
||||
|
||||
f.Fuzz(func(t *testing.T, key string) {
|
||||
// Generate valid JSON with the fuzzed key.
|
||||
// json.Encoder ensures the key is properly escaped (e.g. \x00 becomes \u0000).
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(key); err != nil {
|
||||
return
|
||||
}
|
||||
encodedKey := buf.Bytes()
|
||||
jsonData := fmt.Sprintf(`{%s: "value"}`, encodedKey)
|
||||
|
||||
type Target struct {
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
var got Target
|
||||
if err := Unmarshal([]byte(jsonData), &got); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if got.Field == "value" && key != "field" {
|
||||
t.Errorf("Unmarshal matched key %q (JSON: %s) to field 'field'", key, encodedKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzUnmarshalFieldIsolation(f *testing.F) {
|
||||
// Add seed corpus.
|
||||
f.Add("other", "value")
|
||||
f.Add("safe", "overwrite")
|
||||
f.Add("safe\x00", "attack")
|
||||
|
||||
f.Fuzz(func(t *testing.T, key, val string) {
|
||||
// Generate valid JSON with the fuzzed key and value.
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(key); err != nil {
|
||||
return
|
||||
}
|
||||
encodedKey := buf.Bytes()
|
||||
buf.Reset()
|
||||
if err := enc.Encode(val); err != nil {
|
||||
return
|
||||
}
|
||||
encodedVal := buf.Bytes()
|
||||
|
||||
jsonData := fmt.Sprintf(`{"safe": "value", %s: %s}`, encodedKey, encodedVal)
|
||||
|
||||
type Target struct {
|
||||
Safe string `json:"safe"`
|
||||
}
|
||||
|
||||
var got Target
|
||||
if err := Unmarshal([]byte(jsonData), &got); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if got.Safe != "value" && key != "safe" {
|
||||
t.Errorf("Field 'safe' improperly modified by key %q (JSON: %s). Got %q, want %q", key, encodedKey, got.Safe, "value")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue