VYPR
Moderate severityNVD Advisory· Published Jan 4, 2023· Updated Mar 11, 2025

go-ipld-prime json codec may panic if asked to encode bytes

CVE-2023-22460

Description

go-ipld-prime is an implementation of the InterPlanetary Linked Data (IPLD) spec interfaces, a batteries-included codec implementations of IPLD for CBOR and JSON, and tooling for basic operations on IPLD objects. Encoding data which contains a Bytes kind Node will pass a Bytes token to the JSON encoder which will panic as it doesn't expect to receive Bytes tokens. Such an encode should be treated as an error, as plain JSON should not be able to encode Bytes. This only impacts uses of the json codec. dag-json is not impacted. Use of json as a decoder is not impacted. This issue is fixed in v0.19.0. As a workaround, one may prefer the dag-json codec, which has the ability to encode bytes.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/ipld/go-ipld-primeGo
< 0.19.00.19.0

Affected products

1

Patches

1
146d1c852967

fix: correct json codec links & bytes handling

https://github.com/ipld/go-ipld-primeRod VaggOct 13, 2022via ghsa
3 files changed · +133 42
  • codec/dagjson/marshal.go+36 40 modified
    @@ -202,54 +202,50 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err
     		_, err = sink.Step(&tk)
     		return err
     	case datamodel.Kind_Bytes:
    +		if !options.EncodeBytes {
    +			return fmt.Errorf("cannot marshal IPLD bytes to this codec")
    +		}
     		v, err := n.AsBytes()
     		if err != nil {
     			return err
     		}
    -		if options.EncodeBytes {
    -			// Precisely seven tokens to emit:
    -			tk.Type = tok.TMapOpen
    -			tk.Length = 1
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Type = tok.TString
    -			tk.Str = "/"
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Type = tok.TMapOpen
    -			tk.Length = 1
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Type = tok.TString
    -			tk.Str = "bytes"
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Str = base64.RawStdEncoding.EncodeToString(v)
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Type = tok.TMapClose
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			tk.Type = tok.TMapClose
    -			if _, err = sink.Step(&tk); err != nil {
    -				return err
    -			}
    -			return nil
    -		} else {
    -			tk.Type = tok.TBytes
    -			tk.Bytes = v
    -			_, err = sink.Step(&tk)
    +		// Precisely seven tokens to emit:
    +		tk.Type = tok.TMapOpen
    +		tk.Length = 1
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Type = tok.TString
    +		tk.Str = "/"
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Type = tok.TMapOpen
    +		tk.Length = 1
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Type = tok.TString
    +		tk.Str = "bytes"
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Str = base64.RawStdEncoding.EncodeToString(v)
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Type = tok.TMapClose
    +		if _, err = sink.Step(&tk); err != nil {
    +			return err
    +		}
    +		tk.Type = tok.TMapClose
    +		if _, err = sink.Step(&tk); err != nil {
     			return err
     		}
    +		return nil
     	case datamodel.Kind_Link:
     		if !options.EncodeLinks {
    -			return fmt.Errorf("cannot Marshal ipld links to JSON")
    +			return fmt.Errorf("cannot marshal IPLD links to this codec")
     		}
     		v, err := n.AsLink()
     		if err != nil {
    
  • codec/dagjson/marshal_test.go+46 2 modified
    @@ -12,9 +12,9 @@ import (
     	"github.com/ipld/go-ipld-prime/node/basicnode"
     )
     
    +var link = cid.MustParse("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
    +
     func TestMarshalUndefCid(t *testing.T) {
    -	link, err := cid.Decode("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
    -	qt.Assert(t, err, qt.IsNil)
     	node, err := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) {
     		qp.MapEntry(ma, "UndefCid", qp.Link(cidlink.Link{Cid: cid.Undef}))
     		qp.MapEntry(ma, "DefCid", qp.Link(cidlink.Link{Cid: link}))
    @@ -23,3 +23,47 @@ func TestMarshalUndefCid(t *testing.T) {
     	_, err = ipld.Encode(node, Encode)
     	qt.Assert(t, err, qt.ErrorMatches, "encoding undefined CIDs are not supported by this codec")
     }
    +
    +// mirrored in json but with errors
    +func TestMarshalLinks(t *testing.T) {
    +	linkNode := basicnode.NewLink(cidlink.Link{Cid: link})
    +	mapNode, err := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) {
    +		qp.MapEntry(ma, "Lnk", qp.Node(linkNode))
    +	})
    +	qt.Assert(t, err, qt.IsNil)
    +
    +	t.Run("link dag-json", func(t *testing.T) {
    +		byts, err := ipld.Encode(linkNode, Encode)
    +		qt.Assert(t, err, qt.IsNil)
    +		qt.Assert(t, string(byts), qt.Equals,
    +			`{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}`)
    +	})
    +	t.Run("nested link dag-json", func(t *testing.T) {
    +		byts, err := ipld.Encode(mapNode, Encode)
    +		qt.Assert(t, err, qt.IsNil)
    +		qt.Assert(t, string(byts), qt.Equals,
    +			`{"Lnk":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}}`)
    +	})
    +}
    +
    +// mirrored in json but with errors
    +func TestMarshalBytes(t *testing.T) {
    +	bytsNode := basicnode.NewBytes([]byte("byte me"))
    +	mapNode, err := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) {
    +		qp.MapEntry(ma, "Byts", qp.Node(bytsNode))
    +	})
    +	qt.Assert(t, err, qt.IsNil)
    +
    +	t.Run("bytes dag-json", func(t *testing.T) {
    +		byts, err := ipld.Encode(bytsNode, Encode)
    +		qt.Assert(t, err, qt.IsNil)
    +		qt.Assert(t, string(byts), qt.Equals,
    +			`{"/":{"bytes":"Ynl0ZSBtZQ"}}`)
    +	})
    +	t.Run("nested bytes dag-json", func(t *testing.T) {
    +		byts, err := ipld.Encode(mapNode, Encode)
    +		qt.Assert(t, err, qt.IsNil)
    +		qt.Assert(t, string(byts), qt.Equals,
    +			`{"Byts":{"/":{"bytes":"Ynl0ZSBtZQ"}}}`)
    +	})
    +}
    
  • codec/json/marshal_test.go+51 0 added
    @@ -0,0 +1,51 @@
    +package json
    +
    +import (
    +	"testing"
    +
    +	qt "github.com/frankban/quicktest"
    +	"github.com/ipfs/go-cid"
    +	"github.com/ipld/go-ipld-prime"
    +	"github.com/ipld/go-ipld-prime/datamodel"
    +	"github.com/ipld/go-ipld-prime/fluent/qp"
    +	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    +	"github.com/ipld/go-ipld-prime/node/basicnode"
    +)
    +
    +var link = cid.MustParse("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
    +
    +// mirrored in dag-json but without errors
    +func TestMarshalLinks(t *testing.T) {
    +	linkNode := basicnode.NewLink(cidlink.Link{Cid: link})
    +	mapNode, err := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) {
    +		qp.MapEntry(ma, "Lnk", qp.Node(linkNode))
    +	})
    +	qt.Assert(t, err, qt.IsNil)
    +
    +	t.Run("link json", func(t *testing.T) {
    +		_, err := ipld.Encode(linkNode, Encode)
    +		qt.Assert(t, err, qt.ErrorMatches, "cannot marshal IPLD links to this codec")
    +	})
    +	t.Run("nested link json", func(t *testing.T) {
    +		_, err := ipld.Encode(mapNode, Encode)
    +		qt.Assert(t, err, qt.ErrorMatches, "cannot marshal IPLD links to this codec")
    +	})
    +}
    +
    +// mirrored in dag-json but without errors
    +func TestMarshalBytes(t *testing.T) {
    +	bytsNode := basicnode.NewBytes([]byte("byte me"))
    +	mapNode, err := qp.BuildMap(basicnode.Prototype.Any, -1, func(ma datamodel.MapAssembler) {
    +		qp.MapEntry(ma, "Byts", qp.Node(bytsNode))
    +	})
    +	qt.Assert(t, err, qt.IsNil)
    +
    +	t.Run("bytes json", func(t *testing.T) {
    +		_, err := ipld.Encode(bytsNode, Encode)
    +		qt.Assert(t, err, qt.ErrorMatches, "cannot marshal IPLD bytes to this codec")
    +	})
    +	t.Run("nested bytes json", func(t *testing.T) {
    +		_, err := ipld.Encode(mapNode, Encode)
    +		qt.Assert(t, err, qt.ErrorMatches, "cannot marshal IPLD bytes to this codec")
    +	})
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.