Skip to content

Commit

Permalink
hclsyntax: Don't panic if splat operand is unknown and marked
Browse files Browse the repository at this point in the history
We were calling .Range() on any unknown sourceVal, without first checking
whether it was marked. That method panics if called on a marked value,
so we need to strip that off first.

While testing this I found some return paths that weren't properly
transferring the source value's marks to the output, and so this also
addresses those so that all return paths preserve whatever markings are
present on the source value.

In particular, if a non-list/set/tuple value gets "upgraded" into a tuple
then we must transfer its marks onto the tuple, because the decision about
constructing that value was based on characteristics of the source value.
  • Loading branch information
apparentlymart committed May 9, 2024
1 parent bf54697 commit bc75765
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 7 deletions.
15 changes: 8 additions & 7 deletions hclsyntax/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,7 +1780,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {

if sourceVal.IsNull() {
if autoUpgrade {
return cty.EmptyTupleVal, diags
return cty.EmptyTupleVal.WithSameMarks(sourceVal), diags
}
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Expand All @@ -1798,7 +1798,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
// If we don't even know the _type_ of our source value yet then
// we'll need to defer all processing, since we can't decide our
// result type either.
return cty.DynamicVal, diags
return cty.DynamicVal.WithSameMarks(sourceVal), diags
}

upgradedUnknown := false
Expand All @@ -1813,13 +1813,14 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
// list of a single attribute, but we still need to check if that
// attribute actually exists.
if !sourceVal.IsKnown() {
sourceRng := sourceVal.Range()
unmarkedVal, _ := sourceVal.Unmark()
sourceRng := unmarkedVal.Range()
if sourceRng.CouldBeNull() {
upgradedUnknown = true
}
}

sourceVal = cty.TupleVal([]cty.Value{sourceVal})
sourceVal = cty.TupleVal([]cty.Value{sourceVal}).WithSameMarks(sourceVal)
sourceTy = sourceVal.Type()
}

Expand Down Expand Up @@ -1900,22 +1901,22 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
e.Item.clearValue(ctx) // clean up our temporary value

if upgradedUnknown {
return cty.DynamicVal, diags
return cty.DynamicVal.WithMarks(marks), diags
}

if !isKnown {
// We'll ingore the resultTy diagnostics in this case since they
// will just be the same errors we saw while iterating above.
ty, _ := resultTy()
return cty.UnknownVal(ty), diags
return cty.UnknownVal(ty).WithMarks(marks), diags
}

switch {
case sourceTy.IsListType() || sourceTy.IsSetType():
if len(vals) == 0 {
ty, tyDiags := resultTy()
diags = append(diags, tyDiags...)
return cty.ListValEmpty(ty.ElementType()), diags
return cty.ListValEmpty(ty.ElementType()).WithMarks(marks), diags
}
return cty.ListVal(vals).WithMarks(marks), diags
default:
Expand Down
10 changes: 10 additions & 0 deletions hclsyntax/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,16 @@ upper(
cty.UnknownVal(cty.List(cty.Bool)).RefineNotNull().Mark("sensitive"),
0,
},
{ // splat with sensitive non-collection that's unknown
`not_a_list.*`,
&hcl.EvalContext{
Variables: map[string]cty.Value{
"not_a_list": cty.UnknownVal(cty.EmptyObject).RefineNotNull().Mark("sensitive"),
},
},
cty.TupleVal([]cty.Value{cty.UnknownVal(cty.EmptyObject).RefineNotNull().Mark("sensitive")}).Mark("sensitive"),
0,
},
{ // splat with sensitive collection that's unknown and not null
`maps.*.enabled`,
&hcl.EvalContext{
Expand Down

0 comments on commit bc75765

Please sign in to comment.