From ccab0bf1cf1d679654916e07b831418f14f8a869 Mon Sep 17 00:00:00 2001 From: Kazuma Watanabe Date: Sat, 4 Jan 2025 15:28:42 +0000 Subject: [PATCH] Do not return ephemeral values to unsupported plugins Because ephemeral values are likely to contain secrets, return ErrSensitive for plugins that do not support it to prevent unintended disclosure. --- plugin/server.go | 6 +++++- plugin/server_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/plugin/server.go b/plugin/server.go index be0e8cfca..2f38e1bdf 100644 --- a/plugin/server.go +++ b/plugin/server.go @@ -10,6 +10,7 @@ import ( hcl "github.com/hashicorp/hcl/v2" "github.com/terraform-linters/tflint-plugin-sdk/hclext" "github.com/terraform-linters/tflint-plugin-sdk/plugin/plugin2host" + "github.com/terraform-linters/tflint-plugin-sdk/terraform/lang/marks" sdk "github.com/terraform-linters/tflint-plugin-sdk/tflint" "github.com/terraform-linters/tflint/terraform" "github.com/terraform-linters/tflint/tflint" @@ -145,7 +146,10 @@ func (s *GRPCServer) EvaluateExpr(expr hcl.Expression, opts sdk.EvaluateExprOpti // SDK v0.16+ introduces client-side handling of unknown/NULL/sensitive values. if s.clientSDKVersion != nil && s.clientSDKVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.16.0"))) { - return val, nil + // Before SDK v0.22, ephemeral marks are ignored, so retrun ErrSensitive to prevent secrets ​​from being leaked. + if !marks.Contains(val, marks.Ephemeral) || s.clientSDKVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.22.0"))) { + return val, nil + } } if val.ContainsMarked() { diff --git a/plugin/server_test.go b/plugin/server_test.go index c1482b64a..b93cca0ff 100644 --- a/plugin/server_test.go +++ b/plugin/server_test.go @@ -457,6 +457,11 @@ variable "sensitive" { default = "foo" } +variable "ephemeral" { + ephemeral = true + default = "foo" +} + variable "no_default" {} variable "null" { @@ -472,6 +477,7 @@ variable "foo" { server := NewGRPCServer(runner, rootRunner, runner.Files(), SDKVersion) sdkv15 := version.Must(version.NewVersion("0.15.0")) + sdkv21 := version.Must(version.NewVersion("0.21.0")) // test util functions hclExpr := func(expr string) hcl.Expression { @@ -542,6 +548,15 @@ variable "foo" { return err == nil || !errors.Is(err, sdk.ErrSensitive) }, }, + { + Name: "sensitive value (SDK v0.21)", + Args: func() (hcl.Expression, sdk.EvaluateExprOption) { + return hclExpr(`var.sensitive`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType} + }, + Want: cty.StringVal("foo").Mark(marks.Sensitive), + SDKVersion: sdkv21, + ErrCheck: neverHappend, + }, { Name: "no default", Args: func() (hcl.Expression, sdk.EvaluateExprOption) { @@ -622,6 +637,37 @@ variable "foo" { return err == nil || !errors.Is(err, sdk.ErrNullValue) }, }, + { + Name: "ephemeral value", + Args: func() (hcl.Expression, sdk.EvaluateExprOption) { + return hclExpr(`var.ephemeral`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType} + }, + Want: cty.StringVal("foo").Mark(marks.Ephemeral), + ErrCheck: neverHappend, + }, + { + Name: "ephemeral value (SDK v0.21)", + Args: func() (hcl.Expression, sdk.EvaluateExprOption) { + return hclExpr(`var.ephemeral`), sdk.EvaluateExprOption{WantType: &cty.String, ModuleCtx: sdk.SelfModuleCtxType} + }, + Want: cty.NullVal(cty.NilType), + SDKVersion: sdkv21, + ErrCheck: func(err error) bool { + return err == nil || !errors.Is(err, sdk.ErrSensitive) + }, + }, + { + Name: "ephemeral value in object (SDK v0.21)", + Args: func() (hcl.Expression, sdk.EvaluateExprOption) { + ty := cty.Object(map[string]cty.Type{"value": cty.String}) + return hclExpr(`{ value = var.ephemeral }`), sdk.EvaluateExprOption{WantType: &ty, ModuleCtx: sdk.SelfModuleCtxType} + }, + Want: cty.NullVal(cty.NilType), + SDKVersion: sdkv21, + ErrCheck: func(err error) bool { + return err == nil || !errors.Is(err, sdk.ErrSensitive) + }, + }, } for _, test := range tests {