Skip to content

Commit

Permalink
msgpack: truncate long string prefix refinements
Browse files Browse the repository at this point in the history
Prior to this commit, serialising an unknown value with a string prefix refinement with a sufficiently long prefix would result in an error on deserialisation, since cty limits the size of the refinements blob to 1024 bytes.

We choose an arbitrary maximum length of 256 bytes and truncate any
string prefix refinement on serialisation.

This behaviour is an exception to the general case where refined values
will round-trip losslessly.
  • Loading branch information
kmoe authored and apparentlymart committed Aug 24, 2023
1 parent 7dcbae4 commit 1fd34af
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
57 changes: 56 additions & 1 deletion cty/msgpack/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package msgpack

import (
"fmt"
"strings"
"testing"

"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -50,7 +51,6 @@ func TestRoundTrip(t *testing.T) {
cty.UnknownVal(cty.String).Refine().NotNull().StringPrefix("foo-").NewValue(),
cty.String,
},

{
cty.True,
cty.Bool,
Expand Down Expand Up @@ -350,3 +350,58 @@ func TestRoundTrip(t *testing.T) {
})
}
}

// Unknown values with very long string prefix refinements do not round-trip
// losslessly. If the prefix is longer than 256 bytes it will be truncated to
// a maximum of 256 bytes.
func TestRoundTrip_truncatesStringPrefixRefinement(t *testing.T) {
tests := []struct {
Value cty.Value
Type cty.Type
RoundTripValue cty.Value
}{
{
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("a", 1024)).NewValue(),
cty.String,
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("a", 255)).NewValue(),
},
{
cty.UnknownVal(cty.String).Refine().NotNull().StringPrefix(strings.Repeat("b", 1024)).NewValue(),
cty.String,
cty.UnknownVal(cty.String).Refine().NotNull().StringPrefix(strings.Repeat("b", 255)).NewValue(),
},
{
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("c", 255) + "-").NewValue(),
cty.String,
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("c", 255) + "-").NewValue(),
},
{
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("d", 255) + "🤷🤷").NewValue(),

cty.String,
cty.UnknownVal(cty.String).Refine().StringPrefix(strings.Repeat("d", 255)).NewValue(),
},
}

for _, test := range tests {
t.Run(fmt.Sprintf("%#v as %#v", test.Value, test.Type), func(t *testing.T) {
b, err := Marshal(test.Value, test.Type)
if err != nil {
t.Fatal(err)
}

t.Logf("encoded as %x", b)

got, err := Unmarshal(b, test.Type)
if err != nil {
t.Fatal(err)
}

if !got.RawEquals(test.RoundTripValue) {
t.Errorf(
"unexpected value after round-trip\ninput: %#v\nexpect: %#v\nresult: %#v",
test.Value, test.RoundTripValue, got)
}
})
}
}
11 changes: 11 additions & 0 deletions cty/msgpack/unknown.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/vmihailenco/msgpack/v5"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/ctystrings"
)

type unknownType struct{}
Expand Down Expand Up @@ -85,6 +86,16 @@ func marshalUnknownValue(rng cty.ValueRange, path cty.Path, enc *msgpack.Encoder
}
case rng.TypeConstraint() == cty.String:
if prefix := rng.StringPrefix(); prefix != "" {
// To ensure the total size of the refinements blob does not exceed
// the limit set by our decoder, truncate the prefix string.
// We could allow up to 1018 bytes here if we assume that this
// refinement will only ever be combined with NotNull(), but there
// is no need for such long prefix refinements at the moment.
maxPrefixLength := 256
if len(prefix) > maxPrefixLength {
prefix = prefix[:maxPrefixLength-1]
prefix = ctystrings.SafeKnownPrefix(prefix)
}
mapLen++
refnEnc.EncodeInt(int64(unknownValStringPrefix))
refnEnc.EncodeString(prefix)
Expand Down

0 comments on commit 1fd34af

Please sign in to comment.