From 527cd57ad034b19c3afb943bf9ee514420930fe7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Aug 2025 05:39:10 -0500 Subject: [PATCH 1/3] chore: remove unused code --- types/primitive.go | 70 ---------------------------------------------- 1 file changed, 70 deletions(-) delete mode 100644 types/primitive.go diff --git a/types/primitive.go b/types/primitive.go deleted file mode 100644 index 8f74317..0000000 --- a/types/primitive.go +++ /dev/null @@ -1,70 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - - "github.com/zclconf/go-cty/cty" -) - -func CtyValueStringDefault(def string, val cty.Value) string { - str, err := CtyValueString(val) - if err != nil { - return def - } - return str -} - -// CtyValueString converts a cty.Value to a string. -// It supports only primitive types - bool, number, and string. -// As a special case, it also supports map[string]interface{} with key "value". -func CtyValueString(val cty.Value) (string, error) { - switch { - case val.Type().IsListType(): - vals := val.AsValueSlice() - strs := make([]string, 0, len(vals)) - for _, ele := range vals { - str, err := CtyValueString(ele) - if err != nil { - return "", err - } - strs = append(strs, str) - } - d, _ := json.Marshal(strs) - return string(d), nil - case val.Type().IsMapType(): - output := make(map[string]string) - for k, v := range val.AsValueMap() { - str, err := CtyValueString(v) - if err != nil { - return "", err - } - output[k] = str - } - - d, _ := json.Marshal(output) - return string(d), nil - } - - switch val.Type() { - case cty.Bool: - if val.True() { - return "true", nil - } - return "false", nil - case cty.Number: - return val.AsBigFloat().String(), nil - case cty.String: - //nolint:gocritic // string type asserted above - return val.AsString(), nil - // We may also have a map[string]interface{} with key "value". - case cty.Map(cty.String): - valval, ok := val.AsValueMap()["value"] - if !ok { - return "", fmt.Errorf("map does not have key 'value'") - } - return CtyValueString(valval) - default: - return "", fmt.Errorf("only primitive types are supported - bool, number, and string. Found %s", val.Type().GoString()) - } -} From 948af1376a7c02e10d505f20b6f94b0ec1d9d34b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Aug 2025 06:06:02 -0500 Subject: [PATCH 2/3] chore: remove all possible AsString panics --- extract/parameter.go | 21 +++++++++++++++++++-- hclext/merge.go | 10 +++++----- hclext/references.go | 7 ++++++- init.go | 8 +++++++- paramhook.go | 6 ++++-- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/extract/parameter.go b/extract/parameter.go index d40a6cc..69e651c 100644 --- a/extract/parameter.go +++ b/extract/parameter.go @@ -267,6 +267,7 @@ func optionalStringEnum[T ~string](block *terraform.Block, key string, def T, va func requiredString(block *terraform.Block, key string) (string, *hcl.Diagnostic) { tyAttr := block.GetAttribute(key) tyVal := tyAttr.Value() + if tyVal.Type() != cty.String { typeName := "" if !tyVal.Type().Equals(cty.NilType) { @@ -297,8 +298,24 @@ func requiredString(block *terraform.Block, key string) (string, *hcl.Diagnostic return "", diag } - // nolint:gocritic // string type asserted - return tyVal.AsString(), nil + tyValStr, ok := hclext.AsString(tyVal) + if !ok { + // Either the val is unknown or null + diag := &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Invalid %q attribute for block %s", key, block.Label()), + Detail: "Expected a string, got an unknown or null value", + EvalContext: block.Context().Inner(), + } + + if tyAttr.IsNotNil() { + diag.Subject = &(tyAttr.HCLAttribute().Range) + // diag.Context = &(block.HCLBlock().DefRange) + diag.Expression = tyAttr.HCLAttribute().Expr + } + return "", diag + } + return tyValStr, nil } func optionalBoolean(block *terraform.Block, key string) bool { diff --git a/hclext/merge.go b/hclext/merge.go index caeb762..1c5974e 100644 --- a/hclext/merge.go +++ b/hclext/merge.go @@ -11,12 +11,12 @@ func MergeObjects(a, b cty.Value) cty.Value { output[key] = val } b.ForEachElement(func(key, val cty.Value) (stop bool) { - // TODO: Should this error be captured? - if key.Type() != cty.String { - return true - } //nolint:gocritic // string type asserted above - k := key.AsString() + k, ok := AsString(key) + if !ok { + // TODO: Should this error be captured? + return stop + } old := output[k] if old.IsKnown() && isNotEmptyObject(old) && isNotEmptyObject(val) { output[k] = MergeObjects(old, val) diff --git a/hclext/references.go b/hclext/references.go index ba23e3f..4e61e21 100644 --- a/hclext/references.go +++ b/hclext/references.go @@ -68,7 +68,12 @@ func CreateDotReferenceFromTraversal(traversals ...hcl.Traversal) string { switch { case part.Key.Type().Equals(cty.String): //nolint:gocritic // string type asserted above - refParts = append(refParts, fmt.Sprintf("[%s]", part.Key.AsString())) + stringName, ok := AsString(part.Key) + if !ok { + // Nothing we can do, just put a placeholder + stringName = "??" + } + refParts = append(refParts, fmt.Sprintf("[%s]", stringName)) case part.Key.Type().Equals(cty.Number): idx, _ := part.Key.AsBigFloat().Int64() refParts = append(refParts, fmt.Sprintf("[%d]", idx)) diff --git a/init.go b/init.go index 86eaaad..fdec618 100644 --- a/init.go +++ b/init.go @@ -5,6 +5,8 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" "golang.org/x/xerrors" + + "github.com/coder/preview/hclext" ) // init intends to override some of the default functions afforded by terraform. @@ -27,7 +29,11 @@ func init() { Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { // This code is taken directly from https://github.com/mitchellh/go-homedir/blob/af06845cf3004701891bf4fdb884bfe4920b3727/homedir.go#L58 // The only change is that instead of expanding the path, we return an error - path := args[0].AsString() + path, ok := hclext.AsString(args[0]) + if !ok { + return cty.NilVal, xerrors.Errorf("invalid path argument") + } + if len(path) == 0 { return cty.StringVal(path), nil } diff --git a/paramhook.go b/paramhook.go index 0b8427f..d945750 100644 --- a/paramhook.go +++ b/paramhook.go @@ -39,8 +39,10 @@ func parameterContextsEvalHook(input Input) func(ctx *tfcontext.Context, blocks continue } - //nolint:gocritic // string type asserted - name := nameVal.AsString() + name, ok := hclext.AsString(nameVal) + if !ok { + continue + } var value cty.Value pv, ok := input.RichParameterValue(name) if ok { From e0bd1dc38b0043a1fe2df23c696369c183463239 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 29 Aug 2025 07:00:02 -0500 Subject: [PATCH 3/3] remove comments --- extract/parameter.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/extract/parameter.go b/extract/parameter.go index 69e651c..5516cba 100644 --- a/extract/parameter.go +++ b/extract/parameter.go @@ -283,7 +283,6 @@ func requiredString(block *terraform.Block, key string) (string, *hcl.Diagnostic if tyAttr.IsNotNil() { diag.Subject = &(tyAttr.HCLAttribute().Range) - // diag.Context = &(block.HCLBlock().DefRange) diag.Expression = tyAttr.HCLAttribute().Expr } @@ -310,7 +309,6 @@ func requiredString(block *terraform.Block, key string) (string, *hcl.Diagnostic if tyAttr.IsNotNil() { diag.Subject = &(tyAttr.HCLAttribute().Range) - // diag.Context = &(block.HCLBlock().DefRange) diag.Expression = tyAttr.HCLAttribute().Expr } return "", diag