diff --git a/stream_test.go b/stream_test.go index b6b68e0..7534984 100644 --- a/stream_test.go +++ b/stream_test.go @@ -4,6 +4,7 @@ import ( "archive/tar" "archive/zip" "bytes" + "encoding/binary" "errors" "fmt" "io" @@ -439,6 +440,47 @@ func TestStreamZipChecksumOnSkip(t *testing.T) { } } +func TestStreamZipTruncatedLocalHeader(t *testing.T) { + var buf bytes.Buffer + w := zip.NewWriter(&buf) + file, err := w.Create("root.pem") + if err != nil { + t.Fatal(err) + } + if _, err := file.Write([]byte("certificate content")); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + data := buf.Bytes() + directory := bytes.Index(data, []byte("PK\x01\x02")) + if directory < 0 { + t.Fatal("missing central directory") + } + binary.LittleEndian.PutUint32(data[directory+42:], uint32(len(data))) + for _, skip := range []bool{false, true} { + t.Run(fmt.Sprintf("skip=%v", skip), func(t *testing.T) { + stream, err := OpenStreamBytes("broken.zip", data, StreamOptions{}) + if err != nil { + t.Fatal(err) + } + defer func() { _ = stream.Close() }() + if _, err := stream.Next(); err != nil { + t.Fatal(err) + } + if !skip { + if _, err := io.ReadAll(stream); !errors.Is(err, io.ErrUnexpectedEOF) { + t.Fatalf("Read: %v", err) + } + } + if _, err := stream.Next(); !errors.Is(err, io.ErrUnexpectedEOF) { + t.Fatalf("Next: %v", err) + } + }) + } +} + func TestStreamCondaCombinedLimits(t *testing.T) { data := createTestConda(t) for _, opts := range []StreamOptions{{MaxEntries: 4}, {MaxExpandedBytes: 30}} { diff --git a/stream_zip.go b/stream_zip.go index f7d2e03..09fb4dd 100644 --- a/stream_zip.go +++ b/stream_zip.go @@ -66,6 +66,9 @@ func (z *zipStream) Read(p []byte) (int, error) { if z.current == nil { var err error z.current, err = z.file.Open() + if err == io.EOF { + err = io.ErrUnexpectedEOF + } if err != nil { return 0, err }