From 2b3f3c739460878c7b8f185c68c3d1300a50e92d Mon Sep 17 00:00:00 2001 From: Ian Wahbe Date: Tue, 24 Sep 2024 13:36:28 +0200 Subject: [PATCH] Strip secret values from Configure --- pf/go.mod | 1 + pf/internal/plugin/provider_server.go | 37 ++++++++++++ pf/tests/provider_configure_test.go | 87 +++++++++++++++++++++++++++ pkg/tfbridge/config_encoding.go | 16 ++--- 4 files changed, 133 insertions(+), 8 deletions(-) diff --git a/pf/go.mod b/pf/go.mod index 7925c3c9b1..8132e92961 100644 --- a/pf/go.mod +++ b/pf/go.mod @@ -19,6 +19,7 @@ require ( github.com/pulumi/pulumi-terraform-bridge/v3 v3.91.0 github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.8 github.com/stretchr/testify v1.9.0 + pgregory.net/rapid v0.6.1 ) require ( diff --git a/pf/internal/plugin/provider_server.go b/pf/internal/plugin/provider_server.go index e0c9615b07..a49a6723aa 100644 --- a/pf/internal/plugin/provider_server.go +++ b/pf/internal/plugin/provider_server.go @@ -17,6 +17,7 @@ package plugin import ( "context" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" @@ -80,5 +81,41 @@ func (p providerThunk) Configure( if ctx.Value(setupConfigureKey) != nil { return plugin.ConfigureResponse{}, nil } + req.Inputs = removeSecrets(req.Inputs) + contract.Assertf(!req.Inputs.ContainsSecrets(), + "Inputs to configure should not contain secrets") return p.GrpcProvider.Configure(ctx, req) } + +func removeSecrets(v resource.PropertyMap) resource.PropertyMap { + var remove func(resource.PropertyValue) resource.PropertyValue + remove = func(v resource.PropertyValue) resource.PropertyValue { + switch { + case v.IsArray(): + arr := make([]resource.PropertyValue, 0, len(v.ArrayValue())) + for _, v := range v.ArrayValue() { + arr = append(arr, remove(v)) + } + return resource.NewProperty(arr) + case v.IsObject(): + obj := make(resource.PropertyMap, len(v.ObjectValue())) + for k, v := range v.ObjectValue() { + obj[k] = remove(v) + } + return resource.NewProperty(obj) + case v.IsComputed(): + return resource.MakeComputed(remove(v.Input().Element)) + case v.IsOutput(): + o := v.OutputValue() + o.Secret = false + o.Element = remove(o.Element) + return resource.NewProperty(o) + case v.IsSecret(): + return remove(v.SecretValue().Element) + default: + return v + } + } + + return remove(resource.NewProperty(v)).ObjectValue() +} diff --git a/pf/tests/provider_configure_test.go b/pf/tests/provider_configure_test.go index 0366106803..3ad882417f 100644 --- a/pf/tests/provider_configure_test.go +++ b/pf/tests/provider_configure_test.go @@ -167,3 +167,90 @@ func TestJSONNestedConfigure(t *testing.T) { } }`) } + +func TestJSONNestedConfigureWithSecrets(t *testing.T) { + server, err := newProviderServer(t, testprovider.SyntheticTestBridgeProvider()) + require.NoError(t, err) + replay.ReplaySequence(t, server, ` +[ + { + "method": "/pulumirpc.ResourceProvider/Configure", + "request": { + "args": { + "stringConfigProp": "{\"4dabf18193072939515e22adb298388d\":\"1b47061264138c4ac30d75fd1eb44270\",\"value\":\"secret-example\"}", + "mapNestedProp": "{\"k1\":{\"4dabf18193072939515e22adb298388d\":\"1b47061264138c4ac30d75fd1eb44270\",\"value\":1},\"k2\":2}", + "listNestedProps": "[{\"4dabf18193072939515e22adb298388d\":\"1b47061264138c4ac30d75fd1eb44270\",\"value\":true},false]" + } + }, + "response": { + "supportsPreview": true, + "acceptResources": true + } + }, + { + "method": "/pulumirpc.ResourceProvider/Create", + "request": { + "urn": "urn:pulumi:test-stack::basicprogram::testbridge:index/testres:TestConfigRes::r1", + "preview": false + }, + "response": { + "id": "id-1", + "properties": { + "configCopy": "secret-example", + "id": "id-1" + } + } + } +]`) +} + +func TestConfigureWithSecrets(t *testing.T) { + server, err := newProviderServer(t, testprovider.SyntheticTestBridgeProvider()) + require.NoError(t, err) + replay.ReplaySequence(t, server, ` +[ + { + "method": "/pulumirpc.ResourceProvider/Configure", + "request": { + "args": { + "stringConfigProp": { + "4dabf18193072939515e22adb298388d": "1b47061264138c4ac30d75fd1eb44270", + "value": "secret-example" + }, + "mapNestedProp": { + "k1": { + "4dabf18193072939515e22adb298388d": "1b47061264138c4ac30d75fd1eb44270", + "value": 1 + }, + "k2": 2 + }, + "listNestedProps": [ + { + "4dabf18193072939515e22adb298388d": "1b47061264138c4ac30d75fd1eb44270", + "value": true + }, + false + ] + } + }, + "response": { + "supportsPreview": true, + "acceptResources": true + } + }, + { + "method": "/pulumirpc.ResourceProvider/Create", + "request": { + "urn": "urn:pulumi:test-stack::basicprogram::testbridge:index/testres:TestConfigRes::r1", + "preview": false + }, + "response": { + "id": "id-1", + "properties": { + "configCopy": "secret-example", + "id": "id-1" + } + } + } +]`) +} diff --git a/pkg/tfbridge/config_encoding.go b/pkg/tfbridge/config_encoding.go index ab6a143214..189500c0ae 100644 --- a/pkg/tfbridge/config_encoding.go +++ b/pkg/tfbridge/config_encoding.go @@ -68,13 +68,11 @@ func (*ConfigEncoding) tryUnwrapSecret(encoded any) (any, bool) { } func (enc *ConfigEncoding) convertStringToPropertyValue(s string, typ shim.ValueType) (resource.PropertyValue, error) { - // If the schema expects a string, we can just return this as-is. - if typ == shim.TypeString { - return resource.NewStringProperty(s), nil - } - - // Otherwise, we will attempt to deserialize the input string as JSON and convert the result into a Pulumi + // We attempt to deserialize the input string as JSON and convert the result into a Pulumi // property. If the input string is empty, we will return an appropriate zero value. + // + // We need to do this for all incoming strings, even if the target type is also a string. This is how + // we account for secret or computed string values. if s == "" { return enc.zeroValue(typ), nil } @@ -102,10 +100,12 @@ func (enc *ConfigEncoding) convertStringToPropertyValue(s string, typ shim.Value func (*ConfigEncoding) zeroValue(typ shim.ValueType) resource.PropertyValue { switch typ { + case shim.TypeString: + return resource.NewProperty("") case shim.TypeBool: - return resource.NewPropertyValue(false) + return resource.NewProperty(false) case shim.TypeInt, shim.TypeFloat: - return resource.NewPropertyValue(0) + return resource.NewProperty[float64](0) case shim.TypeList, shim.TypeSet: return resource.NewPropertyValue([]interface{}{}) default: