Skip to content

Commit c0ae2d4

Browse files
committed
Error on conversion to unstructured for invalid json.Marshalers.
If a type's implementation of json.Marshaler returns bytes representing a valid JSON object or array followed by anything other than trailing whitespace, return an error rather than ignoring the trailing data. The documentation for the Marshaler interface indicates that implementations shouldn't do this, but it is safer to check (as json.Marshal does) than to rely on it.
1 parent 92c1d38 commit c0ae2d4

File tree

2 files changed

+34
-22
lines changed

2 files changed

+34
-22
lines changed

Diff for: value/reflectcache.go

+18-19
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package value
1919
import (
2020
"bytes"
2121
"encoding/json"
22+
"errors"
2223
"fmt"
24+
"io"
2325
"reflect"
2426
"sort"
2527
"sync"
@@ -379,33 +381,30 @@ const maxDepth = 10000
379381
// unmarshal unmarshals the given data
380382
// If v is a *map[string]interface{}, numbers are converted to int64 or float64
381383
func unmarshal(data []byte, v interface{}) error {
384+
// Build a decoder from the given data
385+
decoder := json.NewDecoder(bytes.NewBuffer(data))
386+
// Preserve numbers, rather than casting to float64 automatically
387+
decoder.UseNumber()
388+
// Run the decode
389+
if err := decoder.Decode(v); err != nil {
390+
return err
391+
}
392+
next := decoder.InputOffset()
393+
if _, err := decoder.Token(); !errors.Is(err, io.EOF) {
394+
tail := bytes.TrimLeft(data[next:], " \t\r\n")
395+
return fmt.Errorf("unexpected trailing data at offset %d", len(data)-len(tail))
396+
}
397+
398+
// If the decode succeeds, post-process the object to convert json.Number objects to int64 or float64
382399
switch v := v.(type) {
383400
case *map[string]interface{}:
384-
// Build a decoder from the given data
385-
decoder := json.NewDecoder(bytes.NewBuffer(data))
386-
// Preserve numbers, rather than casting to float64 automatically
387-
decoder.UseNumber()
388-
// Run the decode
389-
if err := decoder.Decode(v); err != nil {
390-
return err
391-
}
392-
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
393401
return convertMapNumbers(*v, 0)
394402

395403
case *[]interface{}:
396-
// Build a decoder from the given data
397-
decoder := json.NewDecoder(bytes.NewBuffer(data))
398-
// Preserve numbers, rather than casting to float64 automatically
399-
decoder.UseNumber()
400-
// Run the decode
401-
if err := decoder.Decode(v); err != nil {
402-
return err
403-
}
404-
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
405404
return convertSliceNumbers(*v, 0)
406405

407406
default:
408-
return json.Unmarshal(data, v)
407+
return nil
409408
}
410409
}
411410

Diff for: value/reflectcache_test.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ func (t Time) ToUnstructured() interface{} {
5757

5858
func TestToUnstructured(t *testing.T) {
5959
testcases := []struct {
60-
Data string
61-
Expected interface{}
60+
Data string
61+
Expected interface{}
62+
ExpectedErrorMessage string
6263
}{
6364
{Data: `null`, Expected: nil},
6465
{Data: `true`, Expected: true},
@@ -69,6 +70,12 @@ func TestToUnstructured(t *testing.T) {
6970
{Data: `{"a":1}`, Expected: map[string]interface{}{"a": int64(1)}},
7071
{Data: `0`, Expected: int64(0)},
7172
{Data: `0.0`, Expected: float64(0)},
73+
{Data: "{} \t\r\n", Expected: map[string]interface{}{}},
74+
{Data: "{} \t\r\n}", ExpectedErrorMessage: "error decoding object from json: unexpected trailing data at offset 6"},
75+
{Data: "{} \t\r\n{}", ExpectedErrorMessage: "error decoding object from json: unexpected trailing data at offset 6"},
76+
{Data: "[] \t\r\n", Expected: []interface{}{}},
77+
{Data: "[] \t\r\n]", ExpectedErrorMessage: "error decoding array from json: unexpected trailing data at offset 6"},
78+
{Data: "[] \t\r\n[]", ExpectedErrorMessage: "error decoding array from json: unexpected trailing data at offset 6"},
7279
}
7380

7481
for _, tc := range testcases {
@@ -84,7 +91,13 @@ func TestToUnstructured(t *testing.T) {
8491
rv := reflect.ValueOf(custom)
8592
result, err := TypeReflectEntryOf(rv.Type()).ToUnstructured(rv)
8693
if err != nil {
87-
t.Fatal(err)
94+
if tc.ExpectedErrorMessage == "" {
95+
t.Fatal(err)
96+
} else if got := err.Error(); got != tc.ExpectedErrorMessage {
97+
t.Fatalf("expected error message %q but got %q", tc.ExpectedErrorMessage, got)
98+
}
99+
} else if tc.ExpectedErrorMessage != "" {
100+
t.Fatalf("expected error message %q but got nil error", tc.ExpectedErrorMessage)
88101
}
89102
if !reflect.DeepEqual(result, tc.Expected) {
90103
t.Errorf("expected %#v but got %#v", tc.Expected, result)

0 commit comments

Comments
 (0)