@@ -15,6 +15,8 @@ import (
1515 "github.com/stretchr/testify/require"
1616)
1717
18+ const maxFuzzTypedBytes = 256 << 10
19+
1820func TestSerialization (t * testing.T ) {
1921 raw := []byte ("pineapple" )
2022 wrappedToken , err := tokens .WrapWithType (0 , raw )
@@ -24,3 +26,84 @@ func TestSerialization(t *testing.T) {
2426 assert .Equal (t , driver .Type (0 ), tok .Type )
2527 assert .Equal (t , driver .Token (raw ), tok .Token )
2628}
29+
30+ // TestUnmarshalTypedTokenRejectsTrailingBytes verifies that a valid encoding
31+ // with arbitrary junk appended is rejected rather than silently accepted. See
32+ // issue #2189: ignoring the ASN.1 `rest` slice makes the decoder malleable —
33+ // two distinct byte strings would otherwise decode to the same object.
34+ func TestUnmarshalTypedTokenRejectsTrailingBytes (t * testing.T ) {
35+ valid , err := tokens .WrapWithType (7 , driver .Token ("payload" ))
36+ require .NoError (t , err )
37+
38+ // Sanity: the clean encoding still decodes fine.
39+ _ , err = tokens .UnmarshalTypedToken (valid )
40+ require .NoError (t , err )
41+
42+ withTrailer := append (append ([]byte {}, valid ... ), 0x00 , 0x01 , 0x02 )
43+ got , err := tokens .UnmarshalTypedToken (withTrailer )
44+ require .Error (t , err )
45+ require .Nil (t , got )
46+ require .Contains (t , err .Error (), "trailing bytes" )
47+ }
48+
49+ // TestUnmarshalTypedMetadataRejectsTrailingBytes is the metadata counterpart of
50+ // TestUnmarshalTypedTokenRejectsTrailingBytes.
51+ func TestUnmarshalTypedMetadataRejectsTrailingBytes (t * testing.T ) {
52+ valid , err := tokens .WrapMetadataWithType (7 , driver .Metadata ("payload" ))
53+ require .NoError (t , err )
54+
55+ _ , err = tokens .UnmarshalTypedMetadata (valid )
56+ require .NoError (t , err )
57+
58+ withTrailer := append (append ([]byte {}, valid ... ), 0x00 , 0x01 , 0x02 )
59+ got , err := tokens .UnmarshalTypedMetadata (withTrailer )
60+ require .Error (t , err )
61+ require .Nil (t , got )
62+ require .Contains (t , err .Error (), "trailing bytes" )
63+ }
64+
65+ // FuzzUnmarshalTypedTokenNoPanic fuzzes UnmarshalTypedToken with arbitrary
66+ // bytes. This decoder sits on the receive path for untrusted, ledger-stored
67+ // and peer-supplied token bytes (see issue #2189), so any panic here is an
68+ // unauthenticated DoS against every caller that reads typed tokens.
69+ func FuzzUnmarshalTypedTokenNoPanic (f * testing.F ) {
70+ valid , err := tokens .WrapWithType (7 , driver .Token ("payload" ))
71+ require .NoError (f , err )
72+
73+ f .Add ([]byte (valid ))
74+ f .Add ([]byte {})
75+ f .Add ([]byte ("malformed" ))
76+ f .Add ([]byte (valid )[:len (valid )/ 2 ])
77+ f .Add (append (append ([]byte {}, valid ... ), 0x00 , 0x01 , 0x02 ))
78+
79+ f .Fuzz (func (t * testing.T , raw []byte ) {
80+ if len (raw ) > maxFuzzTypedBytes {
81+ t .Skip ()
82+ }
83+ require .NotPanics (t , func () {
84+ _ , _ = tokens .UnmarshalTypedToken (raw )
85+ })
86+ })
87+ }
88+
89+ // FuzzUnmarshalTypedMetadataNoPanic fuzzes UnmarshalTypedMetadata with
90+ // arbitrary bytes, for the same reasons as FuzzUnmarshalTypedTokenNoPanic.
91+ func FuzzUnmarshalTypedMetadataNoPanic (f * testing.F ) {
92+ valid , err := tokens .WrapMetadataWithType (7 , driver .Metadata ("payload" ))
93+ require .NoError (f , err )
94+
95+ f .Add ([]byte (valid ))
96+ f .Add ([]byte {})
97+ f .Add ([]byte ("malformed" ))
98+ f .Add ([]byte (valid )[:len (valid )/ 2 ])
99+ f .Add (append (append ([]byte {}, valid ... ), 0x00 , 0x01 , 0x02 ))
100+
101+ f .Fuzz (func (t * testing.T , raw []byte ) {
102+ if len (raw ) > maxFuzzTypedBytes {
103+ t .Skip ()
104+ }
105+ require .NotPanics (t , func () {
106+ _ , _ = tokens .UnmarshalTypedMetadata (raw )
107+ })
108+ })
109+ }
0 commit comments