From 9e05eb7fdb902285d1029ecb6f691b8e87ab2e28 Mon Sep 17 00:00:00 2001 From: hc-github-team-secure-vault-core <82990506+hc-github-team-secure-vault-core@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:41:42 -0400 Subject: [PATCH] Backport of Fix panic caused by parsing `json.Number` values for TypeCommaStringSlice fields into release/1.8.x (#14741) * no-op commit due to failed cherry-picking * port over ParseCommaStringSlice json.Number fix from go-secure-stdlib * add changelog entry (cherry picked from commit cec77a20dc8deb514f635064b73a4299085eafec) Co-authored-by: temp Co-authored-by: Chris Capurso <1036769+ccapurso@users.noreply.github.com> --- changelog/14522.txt | 3 + sdk/framework/field_data_test.go | 13 ++ sdk/helper/parseutil/parseutil.go | 5 + sdk/helper/parseutil/parseutil_test.go | 180 +++++++++++++++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 changelog/14522.txt diff --git a/changelog/14522.txt b/changelog/14522.txt new file mode 100644 index 0000000000000..420a9838cf9ff --- /dev/null +++ b/changelog/14522.txt @@ -0,0 +1,3 @@ +```release-note:bug +core: Fix panic caused by parsing JSON integers for fields defined as comma-delimited strings +``` diff --git a/sdk/framework/field_data_test.go b/sdk/framework/field_data_test.go index 7e3c9d1676171..60ffa08232f23 100644 --- a/sdk/framework/field_data_test.go +++ b/sdk/framework/field_data_test.go @@ -1,6 +1,7 @@ package framework import ( + "encoding/json" "net/http" "reflect" "testing" @@ -855,6 +856,18 @@ func TestFieldDataGet(t *testing.T) { false, }, + "comma string slice type, single JSON number value": { + map[string]*FieldSchema{ + "foo": {Type: TypeCommaStringSlice}, + }, + map[string]interface{}{ + "foo": json.Number("123"), + }, + "foo", + []string{"123"}, + false, + }, + "type kv pair, not supplied": { map[string]*FieldSchema{ "foo": {Type: TypeKVPairs}, diff --git a/sdk/helper/parseutil/parseutil.go b/sdk/helper/parseutil/parseutil.go index 405f377099101..e8f06f73806a5 100644 --- a/sdk/helper/parseutil/parseutil.go +++ b/sdk/helper/parseutil/parseutil.go @@ -248,6 +248,11 @@ func ParseString(in interface{}) (string, error) { } func ParseCommaStringSlice(in interface{}) ([]string, error) { + jsonIn, ok := in.(json.Number) + if ok { + in = jsonIn.String() + } + rawString, ok := in.(string) if ok && rawString == "" { return []string{}, nil diff --git a/sdk/helper/parseutil/parseutil_test.go b/sdk/helper/parseutil/parseutil_test.go index a53df904bd170..d96eac1d2ebd0 100644 --- a/sdk/helper/parseutil/parseutil_test.go +++ b/sdk/helper/parseutil/parseutil_test.go @@ -2,6 +2,7 @@ package parseutil import ( "encoding/json" + "math/cmplx" "testing" "time" ) @@ -282,3 +283,182 @@ func Test_ParseBool(t *testing.T) { t.Fatal("wrong output") } } + +func equalStringSlice(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} + +func Test_ParseCommaStringSlice(t *testing.T) { + cases := []struct { + name string + inp interface{} + expected []string + valid bool + }{ + { + "nil", + nil, + []string{}, + true, + }, + { + "empty string", + "", + []string{}, + true, + }, + { + "string without commas", + "foo", + []string{"foo"}, + true, + }, + { + "comma-separated string", + "foo,bar,baz", + []string{"foo", "bar", "baz"}, + true, + }, + { + "comma-separated string with trim", + " foo , bar ,baz ", + []string{"foo", "bar", "baz"}, + true, + }, + { + "json number", + json.Number("123"), + []string{"123"}, + true, + }, + { + "int", + 1, + []string{"1"}, + true, + }, + { + "float", + 5.5, + []string{"5.5"}, + true, + }, + { + "rune", + 'a', + []string{"97"}, + true, + }, + { + "bool", + true, + []string{"1"}, + true, + }, + { + "byte", + byte(10), + []string{"10"}, + true, + }, + { + "complex", + cmplx.Sqrt(-1), + nil, + false, + }, + { + "time", + time.Now(), + nil, + false, + }, + { + "string slice", + []string{"foo", "bar", "baz"}, + []string{"foo", "bar", "baz"}, + true, + }, + { + "json number slice", + []json.Number{json.Number("1"), json.Number("2")}, + []string{"1", "2"}, + true, + }, + { + "int slice", + []int{1, 2, 3}, + []string{"1", "2", "3"}, + true, + }, + { + "float slice", + []float64{1.1, 1.2, 1.3}, + []string{"1.1", "1.2", "1.3"}, + true, + }, + { + "rune slice", + []rune{'a', 'b', 'c'}, + []string{"97", "98", "99"}, + true, + }, + { + "bool slice", + []bool{true, false, true}, + []string{"1", "0", "1"}, + true, + }, + { + "complex slice", + []complex128{cmplx.Sqrt(-1)}, + nil, + false, + }, + { + "map", + map[string]interface{}{"foo": "bar"}, + nil, + false, + }, + { + "struct", + struct{ name string }{"foo"}, + nil, + false, + }, + } + + for _, tc := range cases { + + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + outp, err := ParseCommaStringSlice(tc.inp) + + if tc.valid && err != nil { + t.Errorf("failed to parse: %v. err: %v", tc.inp, err) + } + + if !tc.valid && err == nil { + t.Errorf("no error for: %v", tc.inp) + } + + if !equalStringSlice(outp, tc.expected) { + t.Errorf("input %v parsed as %v, expected %v", tc.inp, outp, tc.expected) + } + }) + } +}