diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3b831d27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +car/car +main +coverage.txt diff --git a/car.go b/car.go index 30fdd344..267237f2 100644 --- a/car.go +++ b/car.go @@ -77,7 +77,7 @@ func ReadHeader(br *bufio.Reader) (*CarHeader, error) { var ch CarHeader if err := cbor.DecodeInto(hb, &ch); err != nil { - return nil, err + return nil, fmt.Errorf("invalid header: %v", err) } return &ch, nil @@ -130,14 +130,14 @@ func NewCarReader(r io.Reader) (*CarReader, error) { return nil, err } - if len(ch.Roots) == 0 { - return nil, fmt.Errorf("empty car") - } - if ch.Version != 1 { return nil, fmt.Errorf("invalid car version: %d", ch.Version) } + if len(ch.Roots) == 0 { + return nil, fmt.Errorf("empty car, no roots") + } + return &CarReader{ br: br, Header: ch, diff --git a/car_test.go b/car_test.go index b50111b2..13f96887 100644 --- a/car_test.go +++ b/car_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/hex" "io" + "strings" "testing" cid "github.com/ipfs/go-cid" @@ -234,3 +235,91 @@ func TestEOFHandling(t *testing.T) { } }) } + +func TestBadHeaders(t *testing.T) { + testCases := []struct { + name string + hex string + errStr string // either the whole error string + errPfx string // or just the prefix + }{ + { + "{version:2}", + "0aa16776657273696f6e02", + "invalid car version: 2", + "", + }, + { + // an unfortunate error because we don't use a pointer + "{roots:[baeaaaa3bmjrq]}", + "13a165726f6f747381d82a480001000003616263", + "invalid car version: 0", + "", + }, { + "{version:\"1\",roots:[baeaaaa3bmjrq]}", + "1da265726f6f747381d82a4800010000036162636776657273696f6e6131", + "", "invalid header: ", + }, { + "{version:1}", + "0aa16776657273696f6e01", + "empty car, no roots", + "", + }, { + "{version:1,roots:{cid:baeaaaa3bmjrq}}", + "20a265726f6f7473a163636964d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, { + "{version:1,roots:[baeaaaa3bmjrq],blip:true}", + "22a364626c6970f565726f6f747381d82a4800010000036162636776657273696f6e01", + "", + "invalid header: ", + }, { + "[1,[]]", + "03820180", + "", + "invalid header: ", + }, { + // this is an unfortunate error, it'd be nice to catch it better but it's + // very unlikely we'd ever see this in practice + "null", + "01f6", + "", + "invalid car version: 0", + }, + } + + makeCar := func(t *testing.T, byts string) error { + fixture, err := hex.DecodeString(byts) + if err != nil { + t.Fatal(err) + } + _, err = NewCarReader(bytes.NewReader(fixture)) + return err + } + + t.Run("Sanity check {version:1,roots:[baeaaaa3bmjrq]}", func(t *testing.T) { + err := makeCar(t, "1ca265726f6f747381d82a4800010000036162636776657273696f6e01") + if err != nil { + t.Fatal(err) + } + }) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := makeCar(t, tc.hex) + if err == nil { + t.Fatal("expected error from bad header, didn't get one") + } + if tc.errStr != "" { + if err.Error() != tc.errStr { + t.Fatalf("bad error: %v", err) + } + } else { + if !strings.HasPrefix(err.Error(), tc.errPfx) { + t.Fatalf("bad error: %v", err) + } + } + }) + } +}