Skip to content

Commit 51b4c32

Browse files
rvaggmasih
andcommitted
feat: improve broken cid.Builder testing for CidBuilder
If you don't use something derived from a cid.Prefix then it'll test execute your hasher to make sure it doesn't error. The reason for this is to avoid more cases where a panic could occur when encoding a ProtoNode. fix: apply code-review feedback Co-authored-by: Masih H. Derkani <[email protected]>
1 parent 0e4726d commit 51b4c32

File tree

2 files changed

+82
-36
lines changed

2 files changed

+82
-36
lines changed

merkledag_test.go

+56-30
Original file line numberDiff line numberDiff line change
@@ -81,38 +81,64 @@ func traverseAndCheck(t *testing.T, root ipld.Node, ds ipld.DAGService, hasF fun
8181
}
8282
}
8383

84+
type brokenBuilder struct{}
85+
86+
func (brokenBuilder) Sum([]byte) (cid.Cid, error) { return cid.Undef, errors.New("Nope!") }
87+
func (brokenBuilder) GetCodec() uint64 { return 0 }
88+
func (b brokenBuilder) WithCodec(uint64) cid.Builder { return b }
89+
8490
func TestBadBuilderEncode(t *testing.T) {
8591
n := NodeWithData([]byte("boop"))
86-
_, err := n.EncodeProtobuf(false)
87-
if err != nil {
88-
t.Fatal(err)
89-
}
90-
err = n.SetCidBuilder(
91-
&cid.Prefix{
92-
MhType: mh.SHA2_256,
93-
MhLength: -1,
94-
Version: 1,
95-
Codec: cid.DagProtobuf,
96-
},
97-
)
98-
if err != nil {
99-
t.Fatal(err)
100-
}
101-
err = n.SetCidBuilder(
102-
&cid.Prefix{
103-
MhType: mh.SHA2_256_TRUNC254_PADDED,
104-
MhLength: 256,
105-
Version: 1,
106-
Codec: cid.DagProtobuf,
107-
},
108-
)
109-
if err == nil {
110-
t.Fatal("expected SetCidBuilder to error on unusable hasher")
111-
}
112-
_, err = n.EncodeProtobuf(false)
113-
if err != nil {
114-
t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err)
115-
}
92+
93+
t.Run("good builder sanity check", func(t *testing.T) {
94+
if _, err := n.EncodeProtobuf(false); err != nil {
95+
t.Fatal(err)
96+
}
97+
if err := n.SetCidBuilder(
98+
&cid.Prefix{
99+
MhType: mh.SHA2_256,
100+
MhLength: -1,
101+
Version: 1,
102+
Codec: cid.DagProtobuf,
103+
},
104+
); err != nil {
105+
t.Fatal(err)
106+
}
107+
})
108+
109+
t.Run("hasher we can't use, should error", func(t *testing.T) {
110+
if err := n.SetCidBuilder(
111+
&cid.Prefix{
112+
MhType: mh.SHA2_256_TRUNC254_PADDED,
113+
MhLength: 256,
114+
Version: 1,
115+
Codec: cid.DagProtobuf,
116+
},
117+
); err == nil {
118+
t.Fatal("expected SetCidBuilder to error on unusable hasher")
119+
}
120+
if _, err := n.EncodeProtobuf(false); err != nil {
121+
t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err)
122+
}
123+
})
124+
125+
t.Run("broken custom builder, should error", func(t *testing.T) {
126+
if err := n.SetCidBuilder(brokenBuilder{}); err == nil {
127+
t.Fatal("expected SetCidBuilder to error on unusable hasher")
128+
}
129+
if _, err := n.EncodeProtobuf(false); err != nil {
130+
t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err)
131+
}
132+
})
133+
134+
t.Run("broken custom builder as pointer, should error", func(t *testing.T) {
135+
if err := n.SetCidBuilder(&brokenBuilder{}); err == nil {
136+
t.Fatal("expected SetCidBuilder to error on unusable hasher")
137+
}
138+
if _, err := n.EncodeProtobuf(false); err != nil {
139+
t.Fatalf("expected EncodeProtobuf to use safe CidBuilder: %v", err)
140+
}
141+
})
116142
}
117143

118144
func TestLinkChecking(t *testing.T) {

node.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ var (
2525
ErrLinkNotFound = fmt.Errorf("no link by that name")
2626
)
2727

28+
// for testing custom CidBuilders
29+
var zeros [256]byte
30+
2831
type immutableProtoNode struct {
2932
encoded []byte
3033
dagpb.PBNode
@@ -106,13 +109,20 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) error {
106109
n.builder = v0CidPrefix
107110
return nil
108111
}
109-
if p, ok := builder.(*cid.Prefix); ok {
110-
mhLen := p.MhLength
111-
if mhLen <= 0 {
112-
mhLen = -1
112+
switch b := builder.(type) {
113+
case cid.Prefix:
114+
if err := checkHasher(b.MhType, b.MhLength); err != nil {
115+
return err
116+
}
117+
case *cid.Prefix:
118+
if err := checkHasher(b.MhType, b.MhLength); err != nil {
119+
return err
113120
}
114-
_, err := mhcore.GetVariableHasher(p.MhType, mhLen)
115-
if err != nil {
121+
default:
122+
// We have to test it's a usable hasher by invoking it and checking it
123+
// doesn't error. This is only a basic check, there are still ways it may
124+
// break
125+
if _, err := builder.Sum(zeros[:]); err != nil {
116126
return err
117127
}
118128
}
@@ -121,6 +131,16 @@ func (n *ProtoNode) SetCidBuilder(builder cid.Builder) error {
121131
return nil
122132
}
123133

134+
// check whether the hasher is likely to be a usable one
135+
func checkHasher(indicator uint64, sizeHint int) error {
136+
mhLen := sizeHint
137+
if mhLen <= 0 {
138+
mhLen = -1
139+
}
140+
_, err := mhcore.GetVariableHasher(indicator, mhLen)
141+
return err
142+
}
143+
124144
// LinkSlice is a slice of format.Links
125145
type LinkSlice []*format.Link
126146

0 commit comments

Comments
 (0)