From b6bfa905db9ba329b2e766c3aaafc1da232e4a45 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 28 Mar 2022 15:24:08 +0200 Subject: [PATCH] Fix slice parsing for plugins --- pkg/plugins/middlewares.go | 2 +- pkg/plugins/plugins.go | 24 +++++++++++++++ pkg/plugins/plugins_test.go | 60 +++++++++++++++++++++++++++++++++++++ pkg/plugins/providers.go | 2 +- 4 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 pkg/plugins/plugins_test.go diff --git a/pkg/plugins/middlewares.go b/pkg/plugins/middlewares.go index 09edbc954..8e476cce3 100644 --- a/pkg/plugins/middlewares.go +++ b/pkg/plugins/middlewares.go @@ -86,7 +86,7 @@ func (p middlewareBuilder) createConfig(config map[string]interface{}) (reflect. vConfig := results[0] cfg := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToSliceHookFunc(","), + DecodeHook: stringToSliceHookFunc, WeaklyTypedInput: true, Result: vConfig.Interface(), } diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index fdfb9fbce..63b513ae8 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "reflect" "strings" "github.com/hashicorp/go-multierror" @@ -166,3 +167,26 @@ func checkLocalPluginManifest(descriptor LocalDescriptor) error { return errs.ErrorOrNil() } + +func stringToSliceHookFunc(f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) { + if f != reflect.String || t != reflect.Slice { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + if strings.Contains(raw, "║") { + values := strings.Split(raw, "║") + // Removes the first value if the slice has a length of 2 and a first value empty. + // It's a workaround to escape the parsing on `,`. + if len(values) == 2 && values[0] == "" { + return values[1:], nil + } + return values, nil + } + + return strings.Split(raw, ","), nil +} diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go new file mode 100644 index 000000000..57d3d3235 --- /dev/null +++ b/pkg/plugins/plugins_test.go @@ -0,0 +1,60 @@ +package plugins + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_stringToSliceHookFunc(t *testing.T) { + testCases := []struct { + desc string + data string + expected []string + }{ + { + desc: "without separator", + data: "abc", + expected: []string{"abc"}, + }, + { + desc: "with the file separator", + data: "a║b║c", + expected: []string{"a", "b", "c"}, + }, + { + desc: "with the label separator", + data: "a,b,c", + expected: []string{"a", "b", "c"}, + }, + { + desc: "with the file separator and values with commas", + data: "a,z║b,w║c,x,y", + expected: []string{"a,z", "b,w", "c,x,y"}, + }, + { + desc: "escaping workaround", + data: "║a,z", + expected: []string{"a,z"}, + }, + { + desc: "with the file separator and empty item", + data: "║a║z", + expected: []string{"", "a", "z"}, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + values, err := stringToSliceHookFunc(reflect.String, reflect.Slice, test.data) + require.NoError(t, err) + + assert.EqualValues(t, test.expected, values) + }) + } +} diff --git a/pkg/plugins/providers.go b/pkg/plugins/providers.go index abec8d1b8..ac9aa296f 100644 --- a/pkg/plugins/providers.go +++ b/pkg/plugins/providers.go @@ -93,7 +93,7 @@ func newProvider(builder providerBuilder, config map[string]interface{}, provide } cfg := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToSliceHookFunc(","), + DecodeHook: stringToSliceHookFunc, WeaklyTypedInput: true, Result: vConfig.Interface(), }