Skip to content

Commit

Permalink
json: Refuse to encode cty.DynamicVal
Browse files Browse the repository at this point in the history
Two different bugs interacted here to cause a cty.DynamicVal to end up
encoding as nothing at all, with no error:
 - The main marshal function was checking for dynamic type before checking
   for unknown value, so cty.DynamicVal was being passed to marshalDynamic.
 - marshalDynamic was not checking the error result from encoding the
   value, and so was successfully returning nothing at all.

This situation will now return the same "value is not known" error that
the function previously returned for all other situations where the
given value is unknown. The JSON representation of cty values can only
represent fully-known values.
  • Loading branch information
apparentlymart committed Mar 20, 2024
1 parent 4b76b75 commit 4a34c33
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* `msgpack`: Now uses string encoding instead of float encoding for a whole number that is too large to fit in any of MessagePack's integer types.
* `function/stdlib`: Type conversion functions (constructed with `MakeToFunc`) can now convert null values of unknown type into null values of the target type, rather than returning an unknown value in that case.
* `json`: Will now correctly reject attempts to encode `cty.DynamicVal`, whereas before it would just produce an invalid JSON document without any error. (This is invalid because JSON encoding cannot support unknown values at all; `cty.DynamicVal` is a special case of unknown value where even the _type_ isn't known.)

# 1.14.3 (February 29, 2024)

Expand Down
12 changes: 7 additions & 5 deletions cty/json/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
if val.IsMarked() {
return path.NewErrorf("value has marks, so it cannot be serialized as JSON")
}
if !val.IsKnown() {
return path.NewErrorf("value is not known")
}

// If we're going to decode as DynamicPseudoType then we need to save
// dynamic type information to recover the real type.
Expand All @@ -24,10 +27,6 @@ func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
return nil
}

if !val.IsKnown() {
return path.NewErrorf("value is not known")
}

// The caller should've guaranteed that the given val is conformant with
// the given type t, so we'll proceed under that assumption here.

Expand Down Expand Up @@ -185,7 +184,10 @@ func marshalDynamic(val cty.Value, path cty.Path, b *bytes.Buffer) error {
return path.NewErrorf("failed to serialize type: %s", err)
}
b.WriteString(`{"value":`)
marshal(val, val.Type(), path, b)
err = marshal(val, val.Type(), path, b)
if err != nil {
return path.NewErrorf("failed to serialize value: %s", err)
}
b.WriteString(`,"type":`)
b.Write(typeJSON)
b.WriteRune('}')
Expand Down

0 comments on commit 4a34c33

Please sign in to comment.