Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions key_collision_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package yaml

import (
"fmt"
"strings"
"testing"
)

func TestRejectCollidingJSONMapKeys(t *testing.T) {
for _, test := range []struct {
name, input, key string
}{
{"fuzz input", " 0:\n 7:\n .0:\n 02", "0"},
{"integer and string", "0x1: integer\n'1': string\n", "1"},
{"boolean and string", "TRUE: boolean\n\"true\": string\n", "true"},
{"nested mapping", "nested: {1.0: number, '1': string}\n", "1"},
{"sequence mapping", "- {1.0: number, '1': string}\n", "1"},
} {
t.Run(test.name, func(t *testing.T) {
want := fmt.Sprintf("multiple YAML keys map to JSON key %q", test.key)
for _, origin := range []bool{false, true} {
t.Run(fmt.Sprintf("origin=%t", origin), func(t *testing.T) {
var out any
_, err := Unmarshal([]byte(test.input), &out, DecodeOpts{Origin: OriginOpt{Enabled: origin}})
if err == nil || !strings.Contains(err.Error(), want) {
t.Fatalf("Unmarshal error = %v, want %q", err, want)
}
})
}
if _, err := YAMLToJSON([]byte(test.input)); err == nil || !strings.Contains(err.Error(), want) {
t.Fatalf("YAMLToJSON error = %v, want %q", err, want)
}
})
}
}

func TestDistinctJSONMapKeys(t *testing.T) {
for _, test := range []struct {
name, input, want string
}{
{"numeric keys", "1: one\n2: two\n", `{"1":"one","2":"two"}`},
{"distinct strings", "'0': one\n'0.0': two\n", `{"0":"one","0.0":"two"}`},
} {
t.Run(test.name, func(t *testing.T) {
for _, origin := range []bool{false, true} {
var out any
if _, err := Unmarshal([]byte(test.input), &out, DecodeOpts{Origin: OriginOpt{Enabled: origin}}); err != nil {
t.Fatalf("Unmarshal with origin=%t: %v", origin, err)
}
}
out, err := YAMLToJSON([]byte(test.input))
if err != nil {
t.Fatal(err)
}
if string(out) != test.want {
t.Fatalf("YAMLToJSON = %s, want %s", out, test.want)
}
})
}
}
3 changes: 3 additions & 0 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (in
return nil, fmt.Errorf("unsupported map key of type: %s, key: %+#v, value: %+#v",
reflect.TypeOf(k), k, v)
}
if _, exists := strMap[keyString]; exists {
return nil, fmt.Errorf("multiple YAML keys map to JSON key %q", keyString)
}
strMap[keyString] = v
}
// replace yamlObj with our new string map
Expand Down