Skip to content

Commit 527ec31

Browse files
wata727apparentlymart
authored andcommitted
hclsyntax: Impose an upper limit on a refined prefix in TemplateExpr
There is no limit to the length of string prefixes produced by template expressions, so in rare cases they may return a refined unknown string has too long a prefix. The cty's msgpack decoder limits the size of an acceptable refinements to 1 kiB, so such a value cannot be handled and an error occurs. This change limits the length of prefixes to 128 B, so overly long prefixes are no longer an issue in most cases.
1 parent 7208bce commit 527ec31

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

hclsyntax/expression_template.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,16 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
9494
ret = cty.UnknownVal(cty.String)
9595
if !diags.HasErrors() { // Invalid input means our partial result buffer is suspect
9696
if knownPrefix := buf.String(); knownPrefix != "" {
97-
ret = ret.Refine().StringPrefix(knownPrefix).NewValue()
97+
byteLen := len(knownPrefix)
98+
// Impose a reasonable upper limit to avoid producing too long a prefix.
99+
// The 128 B is about 10% of the safety limits in cty's msgpack decoder.
100+
// @see https://github.com/zclconf/go-cty/blob/v1.13.2/cty/msgpack/unknown.go#L170-L175
101+
//
102+
// This operation is safe because StringPrefix removes incomplete trailing grapheme clusters.
103+
if byteLen > 128 { // arbitrarily-decided threshold
104+
byteLen = 128
105+
}
106+
ret = ret.Refine().StringPrefix(knownPrefix[:byteLen]).NewValue()
98107
}
99108
}
100109
} else {

hclsyntax/expression_template_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package hclsyntax
55

66
import (
7+
"strings"
78
"testing"
89

910
"github.com/hashicorp/hcl/v2"
@@ -307,6 +308,16 @@ trim`,
307308
cty.UnknownVal(cty.String).Refine().NotNull().StringPrefixFull("test_known_").NewValue(),
308309
0,
309310
},
311+
{ // can preserve a static prefix as a refinement, but the length is limited to 128 B
312+
strings.Repeat("_", 130) + `${unknown}`,
313+
&hcl.EvalContext{
314+
Variables: map[string]cty.Value{
315+
"unknown": cty.UnknownVal(cty.String),
316+
},
317+
},
318+
cty.UnknownVal(cty.String).Refine().NotNull().StringPrefixFull(strings.Repeat("_", 128)).NewValue(),
319+
0,
320+
},
310321
{ // marks from uninterpolated values are ignored
311322
`hello%{ if false } ${target}%{ endif }`,
312323
&hcl.EvalContext{

0 commit comments

Comments
 (0)