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:
Maciej Kisiel 2026-03-12 21:54:23 +01:00 • committed by GitHub
commit 724dd47aa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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")
}
})
}