1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package zip
6
7import (
8	"bytes"
9	"encoding/binary"
10	"encoding/hex"
11	"internal/obscuretestdata"
12	"io"
13	"io/fs"
14	"os"
15	"path/filepath"
16	"reflect"
17	"regexp"
18	"strings"
19	"testing"
20	"testing/fstest"
21	"time"
22)
23
24type ZipTest struct {
25	Name     string
26	Source   func() (r io.ReaderAt, size int64) // if non-nil, used instead of testdata/<Name> file
27	Comment  string
28	File     []ZipTestFile
29	Obscured bool  // needed for Apple notarization (golang.org/issue/34986)
30	Error    error // the error that Opening this file should return
31}
32
33type ZipTestFile struct {
34	Name     string
35	Mode     fs.FileMode
36	NonUTF8  bool
37	ModTime  time.Time
38	Modified time.Time
39
40	// Information describing expected zip file content.
41	// First, reading the entire content should produce the error ContentErr.
42	// Second, if ContentErr==nil, the content should match Content.
43	// If content is large, an alternative to setting Content is to set File,
44	// which names a file in the testdata/ directory containing the
45	// uncompressed expected content.
46	// If content is very large, an alternative to setting Content or File
47	// is to set Size, which will then be checked against the header-reported size
48	// but will bypass the decompressing of the actual data.
49	// This last option is used for testing very large (multi-GB) compressed files.
50	ContentErr error
51	Content    []byte
52	File       string
53	Size       uint64
54}
55
56var tests = []ZipTest{
57	{
58		Name:    "test.zip",
59		Comment: "This is a zipfile comment.",
60		File: []ZipTestFile{
61			{
62				Name:     "test.txt",
63				Content:  []byte("This is a test text file.\n"),
64				Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
65				Mode:     0644,
66			},
67			{
68				Name:     "gophercolor16x16.png",
69				File:     "gophercolor16x16.png",
70				Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
71				Mode:     0644,
72			},
73		},
74	},
75	{
76		Name:    "test-trailing-junk.zip",
77		Comment: "This is a zipfile comment.",
78		File: []ZipTestFile{
79			{
80				Name:     "test.txt",
81				Content:  []byte("This is a test text file.\n"),
82				Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
83				Mode:     0644,
84			},
85			{
86				Name:     "gophercolor16x16.png",
87				File:     "gophercolor16x16.png",
88				Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
89				Mode:     0644,
90			},
91		},
92	},
93	{
94		Name:    "test-prefix.zip",
95		Comment: "This is a zipfile comment.",
96		File: []ZipTestFile{
97			{
98				Name:     "test.txt",
99				Content:  []byte("This is a test text file.\n"),
100				Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
101				Mode:     0644,
102			},
103			{
104				Name:     "gophercolor16x16.png",
105				File:     "gophercolor16x16.png",
106				Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
107				Mode:     0644,
108			},
109		},
110	},
111	{
112		Name:    "test-baddirsz.zip",
113		Comment: "This is a zipfile comment.",
114		File: []ZipTestFile{
115			{
116				Name:     "test.txt",
117				Content:  []byte("This is a test text file.\n"),
118				Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
119				Mode:     0644,
120			},
121			{
122				Name:     "gophercolor16x16.png",
123				File:     "gophercolor16x16.png",
124				Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
125				Mode:     0644,
126			},
127		},
128	},
129	{
130		Name:    "test-badbase.zip",
131		Comment: "This is a zipfile comment.",
132		File: []ZipTestFile{
133			{
134				Name:     "test.txt",
135				Content:  []byte("This is a test text file.\n"),
136				Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
137				Mode:     0644,
138			},
139			{
140				Name:     "gophercolor16x16.png",
141				File:     "gophercolor16x16.png",
142				Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
143				Mode:     0644,
144			},
145		},
146	},
147	{
148		Name:   "r.zip",
149		Source: returnRecursiveZip,
150		File: []ZipTestFile{
151			{
152				Name:     "r/r.zip",
153				Content:  rZipBytes(),
154				Modified: time.Date(2010, 3, 4, 0, 24, 16, 0, time.UTC),
155				Mode:     0666,
156			},
157		},
158	},
159	{
160		Name: "symlink.zip",
161		File: []ZipTestFile{
162			{
163				Name:     "symlink",
164				Content:  []byte("../target"),
165				Modified: time.Date(2012, 2, 3, 19, 56, 48, 0, timeZone(-2*time.Hour)),
166				Mode:     0777 | fs.ModeSymlink,
167			},
168		},
169	},
170	{
171		Name: "readme.zip",
172	},
173	{
174		Name:  "readme.notzip",
175		Error: ErrFormat,
176	},
177	{
178		Name: "dd.zip",
179		File: []ZipTestFile{
180			{
181				Name:     "filename",
182				Content:  []byte("This is a test textfile.\n"),
183				Modified: time.Date(2011, 2, 2, 13, 6, 20, 0, time.UTC),
184				Mode:     0666,
185			},
186		},
187	},
188	{
189		// created in windows XP file manager.
190		Name: "winxp.zip",
191		File: []ZipTestFile{
192			{
193				Name:     "hello",
194				Content:  []byte("world \r\n"),
195				Modified: time.Date(2011, 12, 8, 10, 4, 24, 0, time.UTC),
196				Mode:     0666,
197			},
198			{
199				Name:     "dir/bar",
200				Content:  []byte("foo \r\n"),
201				Modified: time.Date(2011, 12, 8, 10, 4, 50, 0, time.UTC),
202				Mode:     0666,
203			},
204			{
205				Name:     "dir/empty/",
206				Content:  []byte{},
207				Modified: time.Date(2011, 12, 8, 10, 8, 6, 0, time.UTC),
208				Mode:     fs.ModeDir | 0777,
209			},
210			{
211				Name:     "readonly",
212				Content:  []byte("important \r\n"),
213				Modified: time.Date(2011, 12, 8, 10, 6, 8, 0, time.UTC),
214				Mode:     0444,
215			},
216		},
217	},
218	{
219		// created by Zip 3.0 under Linux
220		Name: "unix.zip",
221		File: []ZipTestFile{
222			{
223				Name:     "hello",
224				Content:  []byte("world \r\n"),
225				Modified: time.Date(2011, 12, 8, 10, 4, 24, 0, timeZone(0)),
226				Mode:     0666,
227			},
228			{
229				Name:     "dir/bar",
230				Content:  []byte("foo \r\n"),
231				Modified: time.Date(2011, 12, 8, 10, 4, 50, 0, timeZone(0)),
232				Mode:     0666,
233			},
234			{
235				Name:     "dir/empty/",
236				Content:  []byte{},
237				Modified: time.Date(2011, 12, 8, 10, 8, 6, 0, timeZone(0)),
238				Mode:     fs.ModeDir | 0777,
239			},
240			{
241				Name:     "readonly",
242				Content:  []byte("important \r\n"),
243				Modified: time.Date(2011, 12, 8, 10, 6, 8, 0, timeZone(0)),
244				Mode:     0444,
245			},
246		},
247	},
248	{
249		// created by Go, before we wrote the "optional" data
250		// descriptor signatures (which are required by macOS).
251		// Use obscured file to avoid Apple’s notarization service
252		// rejecting the toolchain due to an inability to unzip this archive.
253		// See golang.org/issue/34986
254		Name:     "go-no-datadesc-sig.zip.base64",
255		Obscured: true,
256		File: []ZipTestFile{
257			{
258				Name:     "foo.txt",
259				Content:  []byte("foo\n"),
260				Modified: time.Date(2012, 3, 8, 16, 59, 10, 0, timeZone(-8*time.Hour)),
261				Mode:     0644,
262			},
263			{
264				Name:     "bar.txt",
265				Content:  []byte("bar\n"),
266				Modified: time.Date(2012, 3, 8, 16, 59, 12, 0, timeZone(-8*time.Hour)),
267				Mode:     0644,
268			},
269		},
270	},
271	{
272		// created by Go, after we wrote the "optional" data
273		// descriptor signatures (which are required by macOS)
274		Name: "go-with-datadesc-sig.zip",
275		File: []ZipTestFile{
276			{
277				Name:     "foo.txt",
278				Content:  []byte("foo\n"),
279				Modified: time.Date(1979, 11, 30, 0, 0, 0, 0, time.UTC),
280				Mode:     0666,
281			},
282			{
283				Name:     "bar.txt",
284				Content:  []byte("bar\n"),
285				Modified: time.Date(1979, 11, 30, 0, 0, 0, 0, time.UTC),
286				Mode:     0666,
287			},
288		},
289	},
290	{
291		Name:   "Bad-CRC32-in-data-descriptor",
292		Source: returnCorruptCRC32Zip,
293		File: []ZipTestFile{
294			{
295				Name:       "foo.txt",
296				Content:    []byte("foo\n"),
297				Modified:   time.Date(1979, 11, 30, 0, 0, 0, 0, time.UTC),
298				Mode:       0666,
299				ContentErr: ErrChecksum,
300			},
301			{
302				Name:     "bar.txt",
303				Content:  []byte("bar\n"),
304				Modified: time.Date(1979, 11, 30, 0, 0, 0, 0, time.UTC),
305				Mode:     0666,
306			},
307		},
308	},
309	// Tests that we verify (and accept valid) crc32s on files
310	// with crc32s in their file header (not in data descriptors)
311	{
312		Name: "crc32-not-streamed.zip",
313		File: []ZipTestFile{
314			{
315				Name:     "foo.txt",
316				Content:  []byte("foo\n"),
317				Modified: time.Date(2012, 3, 8, 16, 59, 10, 0, timeZone(-8*time.Hour)),
318				Mode:     0644,
319			},
320			{
321				Name:     "bar.txt",
322				Content:  []byte("bar\n"),
323				Modified: time.Date(2012, 3, 8, 16, 59, 12, 0, timeZone(-8*time.Hour)),
324				Mode:     0644,
325			},
326		},
327	},
328	// Tests that we verify (and reject invalid) crc32s on files
329	// with crc32s in their file header (not in data descriptors)
330	{
331		Name:   "crc32-not-streamed.zip",
332		Source: returnCorruptNotStreamedZip,
333		File: []ZipTestFile{
334			{
335				Name:       "foo.txt",
336				Content:    []byte("foo\n"),
337				Modified:   time.Date(2012, 3, 8, 16, 59, 10, 0, timeZone(-8*time.Hour)),
338				Mode:       0644,
339				ContentErr: ErrChecksum,
340			},
341			{
342				Name:     "bar.txt",
343				Content:  []byte("bar\n"),
344				Modified: time.Date(2012, 3, 8, 16, 59, 12, 0, timeZone(-8*time.Hour)),
345				Mode:     0644,
346			},
347		},
348	},
349	{
350		Name: "zip64.zip",
351		File: []ZipTestFile{
352			{
353				Name:     "README",
354				Content:  []byte("This small file is in ZIP64 format.\n"),
355				Modified: time.Date(2012, 8, 10, 14, 33, 32, 0, time.UTC),
356				Mode:     0644,
357			},
358		},
359	},
360	// Another zip64 file with different Extras fields. (golang.org/issue/7069)
361	{
362		Name: "zip64-2.zip",
363		File: []ZipTestFile{
364			{
365				Name:     "README",
366				Content:  []byte("This small file is in ZIP64 format.\n"),
367				Modified: time.Date(2012, 8, 10, 14, 33, 32, 0, timeZone(-4*time.Hour)),
368				Mode:     0644,
369			},
370		},
371	},
372	// Largest possible non-zip64 file, with no zip64 header.
373	{
374		Name:   "big.zip",
375		Source: returnBigZipBytes,
376		File: []ZipTestFile{
377			{
378				Name:     "big.file",
379				Content:  nil,
380				Size:     1<<32 - 1,
381				Modified: time.Date(1979, 11, 30, 0, 0, 0, 0, time.UTC),
382				Mode:     0666,
383			},
384		},
385	},
386	{
387		Name: "utf8-7zip.zip",
388		File: []ZipTestFile{
389			{
390				Name:     "世界",
391				Content:  []byte{},
392				Mode:     0666,
393				Modified: time.Date(2017, 11, 6, 13, 9, 27, 867862500, timeZone(-8*time.Hour)),
394			},
395		},
396	},
397	{
398		Name: "utf8-infozip.zip",
399		File: []ZipTestFile{
400			{
401				Name:    "世界",
402				Content: []byte{},
403				Mode:    0644,
404				// Name is valid UTF-8, but format does not have UTF-8 flag set.
405				// We don't do UTF-8 detection for multi-byte runes due to
406				// false-positives with other encodings (e.g., Shift-JIS).
407				// Format says encoding is not UTF-8, so we trust it.
408				NonUTF8:  true,
409				Modified: time.Date(2017, 11, 6, 13, 9, 27, 0, timeZone(-8*time.Hour)),
410			},
411		},
412	},
413	{
414		Name: "utf8-osx.zip",
415		File: []ZipTestFile{
416			{
417				Name:    "世界",
418				Content: []byte{},
419				Mode:    0644,
420				// Name is valid UTF-8, but format does not have UTF-8 set.
421				NonUTF8:  true,
422				Modified: time.Date(2017, 11, 6, 13, 9, 27, 0, timeZone(-8*time.Hour)),
423			},
424		},
425	},
426	{
427		Name: "utf8-winrar.zip",
428		File: []ZipTestFile{
429			{
430				Name:     "世界",
431				Content:  []byte{},
432				Mode:     0666,
433				Modified: time.Date(2017, 11, 6, 13, 9, 27, 867862500, timeZone(-8*time.Hour)),
434			},
435		},
436	},
437	{
438		Name: "utf8-winzip.zip",
439		File: []ZipTestFile{
440			{
441				Name:     "世界",
442				Content:  []byte{},
443				Mode:     0666,
444				Modified: time.Date(2017, 11, 6, 13, 9, 27, 867000000, timeZone(-8*time.Hour)),
445			},
446		},
447	},
448	{
449		Name: "time-7zip.zip",
450		File: []ZipTestFile{
451			{
452				Name:     "test.txt",
453				Content:  []byte{},
454				Size:     1<<32 - 1,
455				Modified: time.Date(2017, 10, 31, 21, 11, 57, 244817900, timeZone(-7*time.Hour)),
456				Mode:     0666,
457			},
458		},
459	},
460	{
461		Name: "time-infozip.zip",
462		File: []ZipTestFile{
463			{
464				Name:     "test.txt",
465				Content:  []byte{},
466				Size:     1<<32 - 1,
467				Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
468				Mode:     0644,
469			},
470		},
471	},
472	{
473		Name: "time-osx.zip",
474		File: []ZipTestFile{
475			{
476				Name:     "test.txt",
477				Content:  []byte{},
478				Size:     1<<32 - 1,
479				Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
480				Mode:     0644,
481			},
482		},
483	},
484	{
485		Name: "time-win7.zip",
486		File: []ZipTestFile{
487			{
488				Name:     "test.txt",
489				Content:  []byte{},
490				Size:     1<<32 - 1,
491				Modified: time.Date(2017, 10, 31, 21, 11, 58, 0, time.UTC),
492				Mode:     0666,
493			},
494		},
495	},
496	{
497		Name: "time-winrar.zip",
498		File: []ZipTestFile{
499			{
500				Name:     "test.txt",
501				Content:  []byte{},
502				Size:     1<<32 - 1,
503				Modified: time.Date(2017, 10, 31, 21, 11, 57, 244817900, timeZone(-7*time.Hour)),
504				Mode:     0666,
505			},
506		},
507	},
508	{
509		Name: "time-winzip.zip",
510		File: []ZipTestFile{
511			{
512				Name:     "test.txt",
513				Content:  []byte{},
514				Size:     1<<32 - 1,
515				Modified: time.Date(2017, 10, 31, 21, 11, 57, 244000000, timeZone(-7*time.Hour)),
516				Mode:     0666,
517			},
518		},
519	},
520	{
521		Name: "time-go.zip",
522		File: []ZipTestFile{
523			{
524				Name:     "test.txt",
525				Content:  []byte{},
526				Size:     1<<32 - 1,
527				Modified: time.Date(2017, 10, 31, 21, 11, 57, 0, timeZone(-7*time.Hour)),
528				Mode:     0666,
529			},
530		},
531	},
532	{
533		Name: "time-22738.zip",
534		File: []ZipTestFile{
535			{
536				Name:     "file",
537				Content:  []byte{},
538				Mode:     0666,
539				Modified: time.Date(1999, 12, 31, 19, 0, 0, 0, timeZone(-5*time.Hour)),
540				ModTime:  time.Date(1999, 12, 31, 19, 0, 0, 0, time.UTC),
541			},
542		},
543	},
544	{
545		Name: "dupdir.zip",
546		File: []ZipTestFile{
547			{
548				Name:     "a/",
549				Content:  []byte{},
550				Mode:     fs.ModeDir | 0666,
551				Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
552			},
553			{
554				Name:     "a/b",
555				Content:  []byte{},
556				Mode:     0666,
557				Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
558			},
559			{
560				Name:     "a/b/",
561				Content:  []byte{},
562				Mode:     fs.ModeDir | 0666,
563				Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
564			},
565			{
566				Name:     "a/b/c",
567				Content:  []byte{},
568				Mode:     0666,
569				Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
570			},
571		},
572	},
573	// Issue 66869: Don't skip over an EOCDR with a truncated comment.
574	// The test file sneakily hides a second EOCDR before the first one;
575	// previously we would extract one file ("file") from this archive,
576	// while most other tools would reject the file or extract a different one ("FILE").
577	{
578		Name:  "comment-truncated.zip",
579		Error: ErrFormat,
580	},
581}
582
583func TestReader(t *testing.T) {
584	for _, zt := range tests {
585		t.Run(zt.Name, func(t *testing.T) {
586			readTestZip(t, zt)
587		})
588	}
589}
590
591func readTestZip(t *testing.T, zt ZipTest) {
592	var z *Reader
593	var err error
594	var raw []byte
595	if zt.Source != nil {
596		rat, size := zt.Source()
597		z, err = NewReader(rat, size)
598		raw = make([]byte, size)
599		if _, err := rat.ReadAt(raw, 0); err != nil {
600			t.Errorf("ReadAt error=%v", err)
601			return
602		}
603	} else {
604		path := filepath.Join("testdata", zt.Name)
605		if zt.Obscured {
606			tf, err := obscuretestdata.DecodeToTempFile(path)
607			if err != nil {
608				t.Errorf("obscuretestdata.DecodeToTempFile(%s): %v", path, err)
609				return
610			}
611			defer os.Remove(tf)
612			path = tf
613		}
614		var rc *ReadCloser
615		rc, err = OpenReader(path)
616		if err == nil {
617			defer rc.Close()
618			z = &rc.Reader
619		}
620		var err2 error
621		raw, err2 = os.ReadFile(path)
622		if err2 != nil {
623			t.Errorf("ReadFile(%s) error=%v", path, err2)
624			return
625		}
626	}
627	if err != zt.Error {
628		t.Errorf("error=%v, want %v", err, zt.Error)
629		return
630	}
631
632	// bail if file is not zip
633	if err == ErrFormat {
634		return
635	}
636
637	// bail here if no Files expected to be tested
638	// (there may actually be files in the zip, but we don't care)
639	if zt.File == nil {
640		return
641	}
642
643	if z.Comment != zt.Comment {
644		t.Errorf("comment=%q, want %q", z.Comment, zt.Comment)
645	}
646	if len(z.File) != len(zt.File) {
647		t.Fatalf("file count=%d, want %d", len(z.File), len(zt.File))
648	}
649
650	// test read of each file
651	for i, ft := range zt.File {
652		readTestFile(t, zt, ft, z.File[i], raw)
653	}
654	if t.Failed() {
655		return
656	}
657
658	// test simultaneous reads
659	n := 0
660	done := make(chan bool)
661	for i := 0; i < 5; i++ {
662		for j, ft := range zt.File {
663			go func(j int, ft ZipTestFile) {
664				readTestFile(t, zt, ft, z.File[j], raw)
665				done <- true
666			}(j, ft)
667			n++
668		}
669	}
670	for ; n > 0; n-- {
671		<-done
672	}
673}
674
675func equalTimeAndZone(t1, t2 time.Time) bool {
676	name1, offset1 := t1.Zone()
677	name2, offset2 := t2.Zone()
678	return t1.Equal(t2) && name1 == name2 && offset1 == offset2
679}
680
681func readTestFile(t *testing.T, zt ZipTest, ft ZipTestFile, f *File, raw []byte) {
682	if f.Name != ft.Name {
683		t.Errorf("name=%q, want %q", f.Name, ft.Name)
684	}
685	if !ft.Modified.IsZero() && !equalTimeAndZone(f.Modified, ft.Modified) {
686		t.Errorf("%s: Modified=%s, want %s", f.Name, f.Modified, ft.Modified)
687	}
688	if !ft.ModTime.IsZero() && !equalTimeAndZone(f.ModTime(), ft.ModTime) {
689		t.Errorf("%s: ModTime=%s, want %s", f.Name, f.ModTime(), ft.ModTime)
690	}
691
692	testFileMode(t, f, ft.Mode)
693
694	size := uint64(f.UncompressedSize)
695	if size == uint32max {
696		size = f.UncompressedSize64
697	} else if size != f.UncompressedSize64 {
698		t.Errorf("%v: UncompressedSize=%#x does not match UncompressedSize64=%#x", f.Name, size, f.UncompressedSize64)
699	}
700
701	// Check that OpenRaw returns the correct byte segment
702	rw, err := f.OpenRaw()
703	if err != nil {
704		t.Errorf("%v: OpenRaw error=%v", f.Name, err)
705		return
706	}
707	start, err := f.DataOffset()
708	if err != nil {
709		t.Errorf("%v: DataOffset error=%v", f.Name, err)
710		return
711	}
712	got, err := io.ReadAll(rw)
713	if err != nil {
714		t.Errorf("%v: OpenRaw ReadAll error=%v", f.Name, err)
715		return
716	}
717	end := uint64(start) + f.CompressedSize64
718	want := raw[start:end]
719	if !bytes.Equal(got, want) {
720		t.Logf("got %q", got)
721		t.Logf("want %q", want)
722		t.Errorf("%v: OpenRaw returned unexpected bytes", f.Name)
723		return
724	}
725
726	r, err := f.Open()
727	if err != nil {
728		t.Errorf("%v", err)
729		return
730	}
731
732	// For very large files, just check that the size is correct.
733	// The content is expected to be all zeros.
734	// Don't bother uncompressing: too big.
735	if ft.Content == nil && ft.File == "" && ft.Size > 0 {
736		if size != ft.Size {
737			t.Errorf("%v: uncompressed size %#x, want %#x", ft.Name, size, ft.Size)
738		}
739		r.Close()
740		return
741	}
742
743	var b bytes.Buffer
744	_, err = io.Copy(&b, r)
745	if err != ft.ContentErr {
746		t.Errorf("copying contents: %v (want %v)", err, ft.ContentErr)
747	}
748	if err != nil {
749		return
750	}
751	r.Close()
752
753	if g := uint64(b.Len()); g != size {
754		t.Errorf("%v: read %v bytes but f.UncompressedSize == %v", f.Name, g, size)
755	}
756
757	var c []byte
758	if ft.Content != nil {
759		c = ft.Content
760	} else if c, err = os.ReadFile("testdata/" + ft.File); err != nil {
761		t.Error(err)
762		return
763	}
764
765	if b.Len() != len(c) {
766		t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c))
767		return
768	}
769
770	for i, b := range b.Bytes() {
771		if b != c[i] {
772			t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i])
773			return
774		}
775	}
776}
777
778func testFileMode(t *testing.T, f *File, want fs.FileMode) {
779	mode := f.Mode()
780	if want == 0 {
781		t.Errorf("%s mode: got %v, want none", f.Name, mode)
782	} else if mode != want {
783		t.Errorf("%s mode: want %v, got %v", f.Name, want, mode)
784	}
785}
786
787func TestInvalidFiles(t *testing.T) {
788	const size = 1024 * 70 // 70kb
789	b := make([]byte, size)
790
791	// zeroes
792	_, err := NewReader(bytes.NewReader(b), size)
793	if err != ErrFormat {
794		t.Errorf("zeroes: error=%v, want %v", err, ErrFormat)
795	}
796
797	// repeated directoryEndSignatures
798	sig := make([]byte, 4)
799	binary.LittleEndian.PutUint32(sig, directoryEndSignature)
800	for i := 0; i < size-4; i += 4 {
801		copy(b[i:i+4], sig)
802	}
803	_, err = NewReader(bytes.NewReader(b), size)
804	if err != ErrFormat {
805		t.Errorf("sigs: error=%v, want %v", err, ErrFormat)
806	}
807
808	// negative size
809	_, err = NewReader(bytes.NewReader([]byte("foobar")), -1)
810	if err == nil {
811		t.Errorf("archive/zip.NewReader: expected error when negative size is passed")
812	}
813}
814
815func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) {
816	data, err := os.ReadFile(filepath.Join("testdata", fileName))
817	if err != nil {
818		panic("Error reading " + fileName + ": " + err.Error())
819	}
820	corrupter(data)
821	return bytes.NewReader(data), int64(len(data))
822}
823
824func returnCorruptCRC32Zip() (r io.ReaderAt, size int64) {
825	return messWith("go-with-datadesc-sig.zip", func(b []byte) {
826		// Corrupt one of the CRC32s in the data descriptor:
827		b[0x2d]++
828	})
829}
830
831func returnCorruptNotStreamedZip() (r io.ReaderAt, size int64) {
832	return messWith("crc32-not-streamed.zip", func(b []byte) {
833		// Corrupt foo.txt's final crc32 byte, in both
834		// the file header and TOC. (0x7e -> 0x7f)
835		b[0x11]++
836		b[0x9d]++
837
838		// TODO(bradfitz): add a new test that only corrupts
839		// one of these values, and verify that that's also an
840		// error. Currently, the reader code doesn't verify the
841		// fileheader and TOC's crc32 match if they're both
842		// non-zero and only the second line above, the TOC,
843		// is what matters.
844	})
845}
846
847// rZipBytes returns the bytes of a recursive zip file, without
848// putting it on disk and triggering certain virus scanners.
849func rZipBytes() []byte {
850	s := `
8510000000 50 4b 03 04 14 00 00 00 08 00 08 03 64 3c f9 f4
8520000010 89 64 48 01 00 00 b8 01 00 00 07 00 00 00 72 2f
8530000020 72 2e 7a 69 70 00 25 00 da ff 50 4b 03 04 14 00
8540000030 00 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00
8550000040 b8 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00
8560000050 2f 00 d0 ff 00 25 00 da ff 50 4b 03 04 14 00 00
8570000060 00 08 00 08 03 64 3c f9 f4 89 64 48 01 00 00 b8
8580000070 01 00 00 07 00 00 00 72 2f 72 2e 7a 69 70 00 2f
8590000080 00 d0 ff c2 54 8e 57 39 00 05 00 fa ff c2 54 8e
8600000090 57 39 00 05 00 fa ff 00 05 00 fa ff 00 14 00 eb
86100000a0 ff c2 54 8e 57 39 00 05 00 fa ff 00 05 00 fa ff
86200000b0 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42
86300000c0 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00 14
86400000d0 00 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21
86500000e0 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 34 00 cb
86600000f0 ff 42 88 21 c4 00 00 00 00 ff ff 00 00 00 ff ff
8670000100 00 34 00 cb ff 42 e8 21 5e 0f 00 00 00 ff ff 0a
8680000110 f0 66 64 12 61 c0 15 dc e8 a0 48 bf 48 af 2a b3
8690000120 20 c0 9b 95 0d c4 67 04 42 53 06 06 06 40 00 06
8700000130 00 f9 ff 6d 01 00 00 00 00 42 e8 21 5e 0f 00 00
8710000140 00 ff ff 0a f0 66 64 12 61 c0 15 dc e8 a0 48 bf
8720000150 48 af 2a b3 20 c0 9b 95 0d c4 67 04 42 53 06 06
8730000160 06 40 00 06 00 f9 ff 6d 01 00 00 00 00 50 4b 01
8740000170 02 14 00 14 00 00 00 08 00 08 03 64 3c f9 f4 89
8750000180 64 48 01 00 00 b8 01 00 00 07 00 00 00 00 00 00
8760000190 00 00 00 00 00 00 00 00 00 00 00 72 2f 72 2e 7a
87700001a0 69 70 50 4b 05 06 00 00 00 00 01 00 01 00 35 00
87800001b0 00 00 6d 01 00 00 00 00`
879	s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "")
880	s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "")
881	b, err := hex.DecodeString(s)
882	if err != nil {
883		panic(err)
884	}
885	return b
886}
887
888func returnRecursiveZip() (r io.ReaderAt, size int64) {
889	b := rZipBytes()
890	return bytes.NewReader(b), int64(len(b))
891}
892
893// biggestZipBytes returns the bytes of a zip file biggest.zip
894// that contains a zip file bigger.zip that contains a zip file
895// big.zip that contains big.file, which contains 2³²-1 zeros.
896// The big.zip file is interesting because it has no zip64 header,
897// much like the innermost zip files in the well-known 42.zip.
898//
899// biggest.zip was generated by changing isZip64 to use > uint32max
900// instead of >= uint32max and then running this program:
901//
902//	package main
903//
904//	import (
905//		"archive/zip"
906//		"bytes"
907//		"io"
908//		"log"
909//		"os"
910//	)
911//
912//	type zeros struct{}
913//
914//	func (zeros) Read(b []byte) (int, error) {
915//		clear(b)
916//		return len(b), nil
917//	}
918//
919//	func main() {
920//		bigZip := makeZip("big.file", io.LimitReader(zeros{}, 1<<32-1))
921//		if err := os.WriteFile("/tmp/big.zip", bigZip, 0666); err != nil {
922//			log.Fatal(err)
923//		}
924//
925//		biggerZip := makeZip("big.zip", bytes.NewReader(bigZip))
926//		if err := os.WriteFile("/tmp/bigger.zip", biggerZip, 0666); err != nil {
927//			log.Fatal(err)
928//		}
929//
930//		biggestZip := makeZip("bigger.zip", bytes.NewReader(biggerZip))
931//		if err := os.WriteFile("/tmp/biggest.zip", biggestZip, 0666); err != nil {
932//			log.Fatal(err)
933//		}
934//	}
935//
936//	func makeZip(name string, r io.Reader) []byte {
937//		var buf bytes.Buffer
938//		w := zip.NewWriter(&buf)
939//		wf, err := w.Create(name)
940//		if err != nil {
941//			log.Fatal(err)
942//		}
943//		if _, err = io.Copy(wf, r); err != nil {
944//			log.Fatal(err)
945//		}
946//		if err := w.Close(); err != nil {
947//			log.Fatal(err)
948//		}
949//		return buf.Bytes()
950//	}
951//
952// The 4 GB of zeros compresses to 4 MB, which compresses to 20 kB,
953// which compresses to 1252 bytes (in the hex dump below).
954//
955// It's here in hex for the same reason as rZipBytes above: to avoid
956// problems with on-disk virus scanners or other zip processors.
957func biggestZipBytes() []byte {
958	s := `
9590000000 50 4b 03 04 14 00 08 00 08 00 00 00 00 00 00 00
9600000010 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 62 69
9610000020 67 67 65 72 2e 7a 69 70 ec dc 6b 4c 53 67 18 07
9620000030 f0 16 c5 ca 65 2e cb b8 94 20 61 1f 44 33 c7 cd
9630000040 c0 86 4a b5 c0 62 8a 61 05 c6 cd 91 b2 54 8c 1b
9640000050 63 8b 03 9c 1b 95 52 5a e3 a0 19 6c b2 05 59 44
9650000060 64 9d 73 83 71 11 46 61 14 b9 1d 14 09 4a c3 60
9660000070 2e 4c 6e a5 60 45 02 62 81 95 b6 94 9e 9e 77 e7
9670000080 d0 43 b6 f8 71 df 96 3c e7 a4 69 ce bf cf e9 79
9680000090 ce ef 79 3f bf f1 31 db b6 bb 31 76 92 e7 f3 07
96900000a0 8b fc 9c ca cc 08 cc cb cc 5e d2 1c 88 d9 7e bb
97000000b0 4f bb 3a 3f 75 f1 5d 7f 8f c2 68 67 77 8f 25 ff
97100000c0 84 e2 93 2d ef a4 95 3d 71 4e 2c b9 b0 87 c3 be
97200000d0 3d f8 a7 60 24 61 c5 ef ae 9e c8 6c 6d 4e 69 c8
97300000e0 67 65 34 f8 37 76 2d 76 5c 54 f3 95 65 49 c7 0f
97400000f0 18 71 4b 7e 5b 6a d1 79 47 61 41 b0 4e 2a 74 45
9750000100 43 58 12 b2 5a a5 c6 7d 68 55 88 d4 98 75 18 6d
9760000110 08 d1 1f 8f 5a 9e 96 ee 45 cf a4 84 4e 4b e8 50
9770000120 a7 13 d9 06 de 52 81 97 36 b2 d7 b8 fc 2b 5f 55
9780000130 23 1f 32 59 cf 30 27 fb e2 8a b9 de 45 dd 63 9c
9790000140 4b b5 8b 96 4c 7a 62 62 cc a1 a7 cf fa f1 fe dd
9800000150 54 62 11 bf 36 78 b3 c7 b1 b5 f2 61 4d 4e dd 66
9810000160 32 2e e6 70 34 5f f4 c9 e6 6c 43 6f da 6b c6 c3
9820000170 09 2c ce 09 57 7f d2 7e b4 23 ba 7c 1b 99 bc 22
9830000180 3e f1 de 91 2f e3 9c 1b 82 cc c2 84 39 aa e6 de
9840000190 b4 69 fc cc cb 72 a6 61 45 f0 d3 1d 26 19 7c 8d
98500001a0 29 c8 66 02 be 77 6a f9 3d 34 79 17 19 c8 96 24
98600001b0 a3 ac e4 dd 3b 1a 8e c6 fe 96 38 6b bf 67 5a 23
98700001c0 f4 16 f4 e6 8a b4 fc c2 cd bf 95 66 1d bb 35 aa
98800001d0 92 7d 66 d8 08 8d a5 1f 54 2a af 09 cf 61 ff d2
98900001e0 85 9d 8f b6 d7 88 07 4a 86 03 db 64 f3 d9 92 73
99000001f0 df ec a7 fc 23 4c 8d 83 79 63 2a d9 fd 8d b3 c8
9910000200 8f 7e d4 19 85 e6 8d 1c 76 f0 8b 58 32 fd 9a d6
9920000210 85 e2 48 ad c3 d5 60 6f 7e 22 dd ef 09 49 7c 7f
9930000220 3a 45 c3 71 b7 df f3 4c 63 fb b5 d9 31 5f 6e d6
9940000230 24 1d a4 4a fe 32 a7 5c 16 48 5c 3e 08 6b 8a d3
9950000240 25 1d a2 12 a5 59 24 ea 20 5f 52 6d ad 94 db 6b
9960000250 94 b9 5d eb 4b a7 5c 44 bb 1e f2 3c 6b cf 52 c9
9970000260 e9 e5 ba 06 b9 c4 e5 0a d0 00 0d d0 00 0d d0 00
9980000270 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d
9990000280 d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0
10000000290 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00 0d d0 00
100100002a0 0d d0 00 cd ff 9e 46 86 fa a7 7d 3a 43 d7 8e 10
100200002b0 52 e9 be e6 6e cf eb 9e 85 4d 65 ce cc 30 c1 44
100300002c0 c0 4e af bc 9c 6c 4b a0 d7 54 ff 1d d5 5c 89 fb
100400002d0 b5 34 7e c4 c2 9e f5 a0 f6 5b 7e 6e ca 73 c7 ef
100500002e0 5d be de f9 e8 81 eb a5 0a a5 63 54 2c d7 1c d1
100600002f0 89 17 85 f8 16 94 f2 8a b2 a3 f5 b6 6d df 75 cd
10070000300 90 dd 64 bd 5d 55 4e f2 55 19 1b b7 cc ef 1b ea
10080000310 2e 05 9c f4 aa 1e a8 cd a6 82 c7 59 0f 5e 9d e0
10090000320 bb fc 6c d6 99 23 eb 36 ad c6 c5 e1 d8 e1 e2 3e
10100000330 d9 90 5a f7 91 5d 6f bc 33 6d 98 47 d2 7c 2e 2f
10110000340 99 a4 25 72 85 49 2c be 0b 5b af 8f e5 6e 81 a6
10120000350 a3 5a 6f 39 53 3a ab 7a 8b 1e 26 f7 46 6c 7d 26
10130000360 53 b3 22 31 94 d3 83 f2 18 4d f5 92 33 27 53 97
10140000370 0f d3 e6 55 9c a6 c5 31 87 6f d3 f3 ae 39 6f 56
10150000380 10 7b ab 7e d0 b4 ca f2 b8 05 be 3f 0e 6e 5a 75
10160000390 ab 0c f5 37 0e ba 8e 75 71 7a aa ed 7a dd 6a 63
101700003a0 be 9b a0 97 27 6a 6f e7 d3 8b c4 7c ec d3 91 56
101800003b0 d9 ac 5e bf 16 42 2f 00 1f 93 a2 23 87 bd e2 59
101900003c0 a0 de 1a 66 c8 62 eb 55 8f 91 17 b4 61 42 7a 50
102000003d0 40 03 34 40 03 34 40 03 34 40 03 34 40 03 34 40
102100003e0 03 34 40 03 34 40 03 34 40 03 34 40 03 34 40 03
102200003f0 34 40 03 34 40 03 34 ff 85 86 90 8b ea 67 90 0d
10230000400 e1 42 1b d2 61 d6 79 ec fd 3e 44 28 a4 51 6c 5c
10240000410 fc d2 72 ca ba 82 18 46 16 61 cd 93 a9 0f d1 24
10250000420 17 99 e2 2c 71 16 84 0c c8 7a 13 0f 9a 5e c5 f0
10260000430 79 64 e2 12 4d c8 82 a1 81 19 2d aa 44 6d 87 54
10270000440 84 71 c1 f6 d4 ca 25 8c 77 b9 08 c7 c8 5e 10 8a
10280000450 8f 61 ed 8c ba 30 1f 79 9a c7 60 34 2b b9 8c f8
10290000460 18 a6 83 1b e3 9f ad 79 fe fd 1b 8b f1 fc 41 6f
10300000470 d4 13 1f e3 b8 83 ba 64 92 e7 eb e4 77 05 8f ba
10310000480 fa 3b 00 00 ff ff 50 4b 07 08 a6 18 b1 91 5e 04
10320000490 00 00 e4 47 00 00 50 4b 01 02 14 00 14 00 08 00
103300004a0 08 00 00 00 00 00 a6 18 b1 91 5e 04 00 00 e4 47
103400004b0 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00
103500004c0 00 00 00 00 62 69 67 67 65 72 2e 7a 69 70 50 4b
103600004d0 05 06 00 00 00 00 01 00 01 00 38 00 00 00 96 04
103700004e0 00 00 00 00`
1038	s = regexp.MustCompile(`[0-9a-f]{7}`).ReplaceAllString(s, "")
1039	s = regexp.MustCompile(`\s+`).ReplaceAllString(s, "")
1040	b, err := hex.DecodeString(s)
1041	if err != nil {
1042		panic(err)
1043	}
1044	return b
1045}
1046
1047func returnBigZipBytes() (r io.ReaderAt, size int64) {
1048	b := biggestZipBytes()
1049	for i := 0; i < 2; i++ {
1050		r, err := NewReader(bytes.NewReader(b), int64(len(b)))
1051		if err != nil {
1052			panic(err)
1053		}
1054		f, err := r.File[0].Open()
1055		if err != nil {
1056			panic(err)
1057		}
1058		b, err = io.ReadAll(f)
1059		if err != nil {
1060			panic(err)
1061		}
1062	}
1063	return bytes.NewReader(b), int64(len(b))
1064}
1065
1066func TestIssue8186(t *testing.T) {
1067	// Directory headers & data found in the TOC of a JAR file.
1068	dirEnts := []string{
1069		"PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\xaa\x1b\x06\xf0\x81\x02\x00\x00\x81\x02\x00\x00-\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00res/drawable-xhdpi-v4/ic_actionbar_accept.png\xfe\xca\x00\x00\x00",
1070		"PK\x01\x02\n\x00\n\x00\x00\b\x00\x004\x9d3?\x90K\x89\xc7t\n\x00\x00t\n\x00\x00\x0e\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd1\x02\x00\x00resources.arsc\x00\x00\x00",
1071		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xff$\x18\xed3\x03\x00\x00\xb4\b\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00t\r\x00\x00AndroidManifest.xml",
1072		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\x14\xc5K\xab\x192\x02\x00\xc8\xcd\x04\x00\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\x10\x00\x00classes.dex",
1073		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?E\x96\nD\xac\x01\x00\x00P\x03\x00\x00&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:C\x02\x00res/layout/actionbar_set_wallpaper.xml",
1074		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?Ļ\x14\xe3\xd8\x01\x00\x00\xd8\x03\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:E\x02\x00res/layout/wallpaper_cropper.xml",
1075		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?}\xc1\x15\x9eZ\x01\x00\x00!\x02\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`G\x02\x00META-INF/MANIFEST.MF",
1076		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xe6\x98Ьo\x01\x00\x00\x84\x02\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfcH\x02\x00META-INF/CERT.SF",
1077		"PK\x01\x02\x14\x00\x14\x00\b\b\b\x004\x9d3?\xbfP\x96b\x86\x04\x00\x00\xb2\x06\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9J\x02\x00META-INF/CERT.RSA",
1078	}
1079	for i, s := range dirEnts {
1080		var f File
1081		err := readDirectoryHeader(&f, strings.NewReader(s))
1082		if err != nil {
1083			t.Errorf("error reading #%d: %v", i, err)
1084		}
1085	}
1086}
1087
1088// Verify we return ErrUnexpectedEOF when length is short.
1089func TestIssue10957(t *testing.T) {
1090	data := []byte("PK\x03\x040000000PK\x01\x0200000" +
1091		"0000000000000000000\x00" +
1092		"\x00\x00\x00\x00\x00000000000000PK\x01" +
1093		"\x020000000000000000000" +
1094		"00000\v\x00\x00\x00\x00\x00000000000" +
1095		"00000000000000PK\x01\x0200" +
1096		"00000000000000000000" +
1097		"00\v\x00\x00\x00\x00\x00000000000000" +
1098		"00000000000PK\x01\x020000<" +
1099		"0\x00\x0000000000000000\v\x00\v" +
1100		"\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
1101		"00000000PK\x01\x0200000000" +
1102		"0000000000000000\v\x00\x00\x00" +
1103		"\x00\x0000PK\x05\x06000000\x05\x00\xfd\x00\x00\x00" +
1104		"\v\x00\x00\x00\x00\x00")
1105	z, err := NewReader(bytes.NewReader(data), int64(len(data)))
1106	if err != nil {
1107		t.Fatal(err)
1108	}
1109	for i, f := range z.File {
1110		r, err := f.Open()
1111		if err != nil {
1112			continue
1113		}
1114		if f.UncompressedSize64 < 1e6 {
1115			n, err := io.Copy(io.Discard, r)
1116			if i == 3 && err != io.ErrUnexpectedEOF {
1117				t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err)
1118			}
1119			if err == nil && uint64(n) != f.UncompressedSize64 {
1120				t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64)
1121			}
1122		}
1123		r.Close()
1124	}
1125}
1126
1127// Verify that this particular malformed zip file is rejected.
1128func TestIssue10956(t *testing.T) {
1129	data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
1130		"0000PK\x05\x06000000000000" +
1131		"0000\v\x00000\x00\x00\x00\x00\x00\x00\x000")
1132	r, err := NewReader(bytes.NewReader(data), int64(len(data)))
1133	if err == nil {
1134		t.Errorf("got nil error, want ErrFormat")
1135	}
1136	if r != nil {
1137		t.Errorf("got non-nil Reader, want nil")
1138	}
1139}
1140
1141// Verify we return ErrUnexpectedEOF when reading truncated data descriptor.
1142func TestIssue11146(t *testing.T) {
1143	data := []byte("PK\x03\x040000000000000000" +
1144		"000000\x01\x00\x00\x000\x01\x00\x00\xff\xff0000" +
1145		"0000000000000000PK\x01\x02" +
1146		"0000\b0\b\x00000000000000" +
1147		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" +
1148		"\x00\x0000\x01\x00\x26\x00\x00\x008\x00\x00\x00\x00\x00")
1149	z, err := NewReader(bytes.NewReader(data), int64(len(data)))
1150	if err != nil {
1151		t.Fatal(err)
1152	}
1153	r, err := z.File[0].Open()
1154	if err != nil {
1155		t.Fatal(err)
1156	}
1157	_, err = io.ReadAll(r)
1158	if err != io.ErrUnexpectedEOF {
1159		t.Errorf("File[0] error = %v; want io.ErrUnexpectedEOF", err)
1160	}
1161	r.Close()
1162}
1163
1164// Verify we do not treat non-zip64 archives as zip64
1165func TestIssue12449(t *testing.T) {
1166	data := []byte{
1167		0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00,
1168		0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46, 0x00, 0x00,
1169		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1170		0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0xca, 0x64,
1171		0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
1172		0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1173		0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
1174		0x00, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x0a,
1175		0x50, 0x4b, 0x07, 0x08, 0x1d, 0x88, 0x77, 0xb0,
1176		0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
1177		0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00,
1178		0x08, 0x00, 0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46,
1179		0x1d, 0x88, 0x77, 0xb0, 0x07, 0x00, 0x00, 0x00,
1180		0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00,
1181		0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1182		0xa0, 0x81, 0x00, 0x00, 0x00, 0x00, 0xca, 0x64,
1183		0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
1184		0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1185		0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
1186		0x00, 0x97, 0x2b, 0x49, 0x23, 0x05, 0xc5, 0x0b,
1187		0xa7, 0xd1, 0x52, 0xa2, 0x9c, 0x50, 0x4b, 0x06,
1188		0x07, 0xc8, 0x19, 0xc1, 0xaf, 0x94, 0x9c, 0x61,
1189		0x44, 0xbe, 0x94, 0x19, 0x42, 0x58, 0x12, 0xc6,
1190		0x5b, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
1191		0x00, 0x01, 0x00, 0x01, 0x00, 0x69, 0x00, 0x00,
1192		0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
1193	}
1194	// Read in the archive.
1195	_, err := NewReader(bytes.NewReader(data), int64(len(data)))
1196	if err != nil {
1197		t.Errorf("Error reading the archive: %v", err)
1198	}
1199}
1200
1201func TestFS(t *testing.T) {
1202	for _, test := range []struct {
1203		file string
1204		want []string
1205	}{
1206		{
1207			"testdata/unix.zip",
1208			[]string{"hello", "dir/bar", "readonly"},
1209		},
1210		{
1211			"testdata/subdir.zip",
1212			[]string{"a/b/c"},
1213		},
1214	} {
1215		test := test
1216		t.Run(test.file, func(t *testing.T) {
1217			t.Parallel()
1218			z, err := OpenReader(test.file)
1219			if err != nil {
1220				t.Fatal(err)
1221			}
1222			defer z.Close()
1223			if err := fstest.TestFS(z, test.want...); err != nil {
1224				t.Error(err)
1225			}
1226		})
1227	}
1228}
1229
1230func TestFSWalk(t *testing.T) {
1231	for _, test := range []struct {
1232		file    string
1233		want    []string
1234		wantErr bool
1235	}{
1236		{
1237			file: "testdata/unix.zip",
1238			want: []string{".", "dir", "dir/bar", "dir/empty", "hello", "readonly"},
1239		},
1240		{
1241			file: "testdata/subdir.zip",
1242			want: []string{".", "a", "a/b", "a/b/c"},
1243		},
1244		{
1245			file:    "testdata/dupdir.zip",
1246			wantErr: true,
1247		},
1248	} {
1249		test := test
1250		t.Run(test.file, func(t *testing.T) {
1251			t.Parallel()
1252			z, err := OpenReader(test.file)
1253			if err != nil {
1254				t.Fatal(err)
1255			}
1256			var files []string
1257			sawErr := false
1258			err = fs.WalkDir(z, ".", func(path string, d fs.DirEntry, err error) error {
1259				if err != nil {
1260					if !test.wantErr {
1261						t.Errorf("%s: %v", path, err)
1262					}
1263					sawErr = true
1264					return nil
1265				}
1266				files = append(files, path)
1267				return nil
1268			})
1269			if err != nil {
1270				t.Errorf("fs.WalkDir error: %v", err)
1271			}
1272			if test.wantErr && !sawErr {
1273				t.Error("succeeded but want error")
1274			} else if !test.wantErr && sawErr {
1275				t.Error("unexpected error")
1276			}
1277			if test.want != nil && !reflect.DeepEqual(files, test.want) {
1278				t.Errorf("got %v want %v", files, test.want)
1279			}
1280		})
1281	}
1282}
1283
1284func TestFSModTime(t *testing.T) {
1285	t.Parallel()
1286	z, err := OpenReader("testdata/subdir.zip")
1287	if err != nil {
1288		t.Fatal(err)
1289	}
1290	defer z.Close()
1291
1292	for _, test := range []struct {
1293		name string
1294		want time.Time
1295	}{
1296		{
1297			"a",
1298			time.Date(2021, 4, 19, 12, 29, 56, 0, timeZone(-7*time.Hour)).UTC(),
1299		},
1300		{
1301			"a/b/c",
1302			time.Date(2021, 4, 19, 12, 29, 59, 0, timeZone(-7*time.Hour)).UTC(),
1303		},
1304	} {
1305		fi, err := fs.Stat(z, test.name)
1306		if err != nil {
1307			t.Errorf("%s: %v", test.name, err)
1308			continue
1309		}
1310		if got := fi.ModTime(); !got.Equal(test.want) {
1311			t.Errorf("%s: got modtime %v, want %v", test.name, got, test.want)
1312		}
1313	}
1314}
1315
1316func TestCVE202127919(t *testing.T) {
1317	t.Setenv("GODEBUG", "zipinsecurepath=0")
1318	// Archive containing only the file "../test.txt"
1319	data := []byte{
1320		0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00,
1321		0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1322		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1323		0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2e, 0x2e,
1324		0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78,
1325		0x74, 0x0a, 0xc9, 0xc8, 0x2c, 0x56, 0xc8, 0x2c,
1326		0x56, 0x48, 0x54, 0x28, 0x49, 0x2d, 0x2e, 0x51,
1327		0x28, 0x49, 0xad, 0x28, 0x51, 0x48, 0xcb, 0xcc,
1328		0x49, 0xd5, 0xe3, 0x02, 0x04, 0x00, 0x00, 0xff,
1329		0xff, 0x50, 0x4b, 0x07, 0x08, 0xc0, 0xd7, 0xed,
1330		0xc3, 0x20, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00,
1331		0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14,
1332		0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
1333		0x00, 0xc0, 0xd7, 0xed, 0xc3, 0x20, 0x00, 0x00,
1334		0x00, 0x1a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
1335		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1336		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
1337		0x2e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
1338		0x78, 0x74, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00,
1339		0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x39, 0x00,
1340		0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00,
1341	}
1342	r, err := NewReader(bytes.NewReader(data), int64(len(data)))
1343	if err != ErrInsecurePath {
1344		t.Fatalf("Error reading the archive: %v", err)
1345	}
1346	_, err = r.Open("test.txt")
1347	if err != nil {
1348		t.Errorf("Error reading file: %v", err)
1349	}
1350	if len(r.File) != 1 {
1351		t.Fatalf("No entries in the file list")
1352	}
1353	if r.File[0].Name != "../test.txt" {
1354		t.Errorf("Unexpected entry name: %s", r.File[0].Name)
1355	}
1356	if _, err := r.File[0].Open(); err != nil {
1357		t.Errorf("Error opening file: %v", err)
1358	}
1359}
1360
1361func TestOpenReaderInsecurePath(t *testing.T) {
1362	t.Setenv("GODEBUG", "zipinsecurepath=0")
1363	// Archive containing only the file "../test.txt"
1364	data := []byte{
1365		0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00,
1366		0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1367		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1368		0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2e, 0x2e,
1369		0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78,
1370		0x74, 0x0a, 0xc9, 0xc8, 0x2c, 0x56, 0xc8, 0x2c,
1371		0x56, 0x48, 0x54, 0x28, 0x49, 0x2d, 0x2e, 0x51,
1372		0x28, 0x49, 0xad, 0x28, 0x51, 0x48, 0xcb, 0xcc,
1373		0x49, 0xd5, 0xe3, 0x02, 0x04, 0x00, 0x00, 0xff,
1374		0xff, 0x50, 0x4b, 0x07, 0x08, 0xc0, 0xd7, 0xed,
1375		0xc3, 0x20, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00,
1376		0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14,
1377		0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
1378		0x00, 0xc0, 0xd7, 0xed, 0xc3, 0x20, 0x00, 0x00,
1379		0x00, 0x1a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
1380		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1381		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
1382		0x2e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74,
1383		0x78, 0x74, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00,
1384		0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x39, 0x00,
1385		0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00,
1386	}
1387
1388	// Read in the archive with the OpenReader interface
1389	name := filepath.Join(t.TempDir(), "test.zip")
1390	err := os.WriteFile(name, data, 0644)
1391	if err != nil {
1392		t.Fatalf("Unable to write out the bugos zip entry")
1393	}
1394	r, err := OpenReader(name)
1395	if r != nil {
1396		defer r.Close()
1397	}
1398
1399	if err != ErrInsecurePath {
1400		t.Fatalf("Error reading the archive, we expected ErrInsecurePath but got: %v", err)
1401	}
1402	_, err = r.Open("test.txt")
1403	if err != nil {
1404		t.Errorf("Error reading file: %v", err)
1405	}
1406	if len(r.File) != 1 {
1407		t.Fatalf("No entries in the file list")
1408	}
1409	if r.File[0].Name != "../test.txt" {
1410		t.Errorf("Unexpected entry name: %s", r.File[0].Name)
1411	}
1412	if _, err := r.File[0].Open(); err != nil {
1413		t.Errorf("Error opening file: %v", err)
1414	}
1415}
1416
1417func TestCVE202133196(t *testing.T) {
1418	// Archive that indicates it has 1 << 128 -1 files,
1419	// this would previously cause a panic due to attempting
1420	// to allocate a slice with 1 << 128 -1 elements.
1421	data := []byte{
1422		0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
1423		0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1424		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1425		0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02,
1426		0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00,
1427		0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20,
1428		0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
1429		0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
1430		0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
1431		0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00,
1432		0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00,
1433		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1434		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1435		0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c,
1436		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d,
1437		0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1438		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
1439		0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
1440		0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00,
1441		0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00,
1442		0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00,
1443		0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
1444		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
1445		0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff,
1446		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1447		0xff, 0xff, 0xff, 0x00, 0x00,
1448	}
1449	_, err := NewReader(bytes.NewReader(data), int64(len(data)))
1450	if err != ErrFormat {
1451		t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
1452	}
1453
1454	// Also check that an archive containing a handful of empty
1455	// files doesn't cause an issue
1456	b := bytes.NewBuffer(nil)
1457	w := NewWriter(b)
1458	for i := 0; i < 5; i++ {
1459		_, err := w.Create("")
1460		if err != nil {
1461			t.Fatalf("Writer.Create failed: %s", err)
1462		}
1463	}
1464	if err := w.Close(); err != nil {
1465		t.Fatalf("Writer.Close failed: %s", err)
1466	}
1467	r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
1468	if err != nil {
1469		t.Fatalf("NewReader failed: %s", err)
1470	}
1471	if len(r.File) != 5 {
1472		t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
1473	}
1474}
1475
1476func TestCVE202139293(t *testing.T) {
1477	// directory size is so large, that the check in Reader.init
1478	// overflows when subtracting from the archive size, causing
1479	// the pre-allocation check to be bypassed.
1480	data := []byte{
1481		0x50, 0x4b, 0x06, 0x06, 0x05, 0x06, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
1482		0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1483		0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
1484		0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1485		0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
1486		0xff, 0x50, 0xfe, 0x00, 0xff, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xff,
1487	}
1488	_, err := NewReader(bytes.NewReader(data), int64(len(data)))
1489	if err != ErrFormat {
1490		t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
1491	}
1492}
1493
1494func TestCVE202141772(t *testing.T) {
1495	t.Setenv("GODEBUG", "zipinsecurepath=0")
1496	// Archive contains a file whose name is exclusively made up of '/', '\'
1497	// characters, or "../", "..\" paths, which would previously cause a panic.
1498	//
1499	//  Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
1500	// --------  ------  ------- ---- ---------- ----- --------  ----
1501	//        0  Stored        0   0% 08-05-2021 18:32 00000000  /
1502	//        0  Stored        0   0% 09-14-2021 12:59 00000000  //
1503	//        0  Stored        0   0% 09-14-2021 12:59 00000000  \
1504	//       11  Stored       11   0% 09-14-2021 13:04 0d4a1185  /test.txt
1505	// --------          -------  ---                            -------
1506	//       11               11   0%                            4 files
1507	data := []byte{
1508		0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x08,
1509		0x00, 0x00, 0x06, 0x94, 0x05, 0x53, 0x00, 0x00,
1510		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1511		0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x50,
1512		0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
1513		0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
1514		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1515		0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x50,
1516		0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
1517		0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
1518		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1519		0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x50, 0x4b,
1520		0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
1521		0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, 0x4a, 0x0d,
1522		0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
1523		0x09, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
1524		0x74, 0x2e, 0x74, 0x78, 0x74, 0x68, 0x65, 0x6c,
1525		0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
1526		0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x0a, 0x00,
1527		0x00, 0x08, 0x00, 0x00, 0x06, 0x94, 0x05, 0x53,
1528		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1529		0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
1530		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
1531		0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x50,
1532		0x4b, 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00,
1533		0x00, 0x00, 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00,
1534		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1535		0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00,
1536		0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
1537		0x00, 0x1f, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x0a,
1538		0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1539		0x00, 0x18, 0x00, 0x93, 0x98, 0x25, 0x57, 0x25,
1540		0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
1541		0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
1542		0xa9, 0xd7, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x3f,
1543		0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
1544		0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
1545		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
1546		0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1547		0x00, 0x20, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00,
1548		0x00, 0x5c, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00,
1549		0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x93, 0x98,
1550		0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
1551		0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
1552		0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x50, 0x4b,
1553		0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00,
1554		0x00, 0x00, 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11,
1555		0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00,
1556		0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x00, 0x00,
1557		0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
1558		0x5e, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
1559		0x74, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20,
1560		0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18,
1561		0x00, 0xa9, 0x80, 0x51, 0x01, 0x26, 0xa9, 0xd7,
1562		0x01, 0x31, 0xd1, 0x57, 0x01, 0x26, 0xa9, 0xd7,
1563		0x01, 0xdf, 0x48, 0x85, 0xf9, 0x25, 0xa9, 0xd7,
1564		0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
1565		0x00, 0x04, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00,
1566		0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
1567	}
1568	r, err := NewReader(bytes.NewReader(data), int64(len(data)))
1569	if err != ErrInsecurePath {
1570		t.Fatalf("Error reading the archive: %v", err)
1571	}
1572	entryNames := []string{`/`, `//`, `\`, `/test.txt`}
1573	var names []string
1574	for _, f := range r.File {
1575		names = append(names, f.Name)
1576		if _, err := f.Open(); err != nil {
1577			t.Errorf("Error opening %q: %v", f.Name, err)
1578		}
1579		if _, err := r.Open(f.Name); err == nil {
1580			t.Errorf("Opening %q with fs.FS API succeeded", f.Name)
1581		}
1582	}
1583	if !reflect.DeepEqual(names, entryNames) {
1584		t.Errorf("Unexpected file entries: %q", names)
1585	}
1586	if _, err := r.Open(""); err == nil {
1587		t.Errorf("Opening %q with fs.FS API succeeded", "")
1588	}
1589	if _, err := r.Open("test.txt"); err != nil {
1590		t.Errorf("Error opening %q with fs.FS API: %v", "test.txt", err)
1591	}
1592	dirEntries, err := fs.ReadDir(r, ".")
1593	if err != nil {
1594		t.Fatalf("Error reading the root directory: %v", err)
1595	}
1596	if len(dirEntries) != 1 || dirEntries[0].Name() != "test.txt" {
1597		t.Errorf("Unexpected directory entries")
1598		for _, dirEntry := range dirEntries {
1599			_, err := r.Open(dirEntry.Name())
1600			t.Logf("%q (Open error: %v)", dirEntry.Name(), err)
1601		}
1602		t.FailNow()
1603	}
1604	info, err := dirEntries[0].Info()
1605	if err != nil {
1606		t.Fatalf("Error reading info entry: %v", err)
1607	}
1608	if name := info.Name(); name != "test.txt" {
1609		t.Errorf("Inconsistent name in info entry: %v", name)
1610	}
1611}
1612
1613func TestUnderSize(t *testing.T) {
1614	z, err := OpenReader("testdata/readme.zip")
1615	if err != nil {
1616		t.Fatal(err)
1617	}
1618	defer z.Close()
1619
1620	for _, f := range z.File {
1621		f.UncompressedSize64 = 1
1622	}
1623
1624	for _, f := range z.File {
1625		t.Run(f.Name, func(t *testing.T) {
1626			rd, err := f.Open()
1627			if err != nil {
1628				t.Fatal(err)
1629			}
1630			defer rd.Close()
1631
1632			_, err = io.Copy(io.Discard, rd)
1633			if err != ErrFormat {
1634				t.Fatalf("Error mismatch\n\tGot:  %v\n\tWant: %v", err, ErrFormat)
1635			}
1636		})
1637	}
1638}
1639
1640func TestIssue54801(t *testing.T) {
1641	for _, input := range []string{"testdata/readme.zip", "testdata/dd.zip"} {
1642		z, err := OpenReader(input)
1643		if err != nil {
1644			t.Fatal(err)
1645		}
1646		defer z.Close()
1647
1648		for _, f := range z.File {
1649			// Make file a directory
1650			f.Name += "/"
1651
1652			t.Run(f.Name, func(t *testing.T) {
1653				t.Logf("CompressedSize64: %d, Flags: %#x", f.CompressedSize64, f.Flags)
1654
1655				rd, err := f.Open()
1656				if err != nil {
1657					t.Fatal(err)
1658				}
1659				defer rd.Close()
1660
1661				n, got := io.Copy(io.Discard, rd)
1662				if n != 0 || got != ErrFormat {
1663					t.Fatalf("Error mismatch, got: %d, %v, want: %v", n, got, ErrFormat)
1664				}
1665			})
1666		}
1667	}
1668}
1669
1670func TestInsecurePaths(t *testing.T) {
1671	t.Setenv("GODEBUG", "zipinsecurepath=0")
1672	for _, path := range []string{
1673		"../foo",
1674		"/foo",
1675		"a/b/../../../c",
1676		`a\b`,
1677	} {
1678		var buf bytes.Buffer
1679		zw := NewWriter(&buf)
1680		_, err := zw.Create(path)
1681		if err != nil {
1682			t.Errorf("zw.Create(%q) = %v", path, err)
1683			continue
1684		}
1685		zw.Close()
1686
1687		zr, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
1688		if err != ErrInsecurePath {
1689			t.Errorf("NewReader for archive with file %q: got err %v, want ErrInsecurePath", path, err)
1690			continue
1691		}
1692		var gotPaths []string
1693		for _, f := range zr.File {
1694			gotPaths = append(gotPaths, f.Name)
1695		}
1696		if !reflect.DeepEqual(gotPaths, []string{path}) {
1697			t.Errorf("NewReader for archive with file %q: got files %q", path, gotPaths)
1698			continue
1699		}
1700	}
1701}
1702
1703func TestDisableInsecurePathCheck(t *testing.T) {
1704	t.Setenv("GODEBUG", "zipinsecurepath=1")
1705	var buf bytes.Buffer
1706	zw := NewWriter(&buf)
1707	const name = "/foo"
1708	_, err := zw.Create(name)
1709	if err != nil {
1710		t.Fatalf("zw.Create(%q) = %v", name, err)
1711	}
1712	zw.Close()
1713	zr, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
1714	if err != nil {
1715		t.Fatalf("NewReader with zipinsecurepath=1: got err %v, want nil", err)
1716	}
1717	var gotPaths []string
1718	for _, f := range zr.File {
1719		gotPaths = append(gotPaths, f.Name)
1720	}
1721	if want := []string{name}; !reflect.DeepEqual(gotPaths, want) {
1722		t.Errorf("NewReader with zipinsecurepath=1: got files %q, want %q", gotPaths, want)
1723	}
1724}
1725
1726func TestCompressedDirectory(t *testing.T) {
1727	// Empty Java JAR, with a compressed directory with uncompressed size 0
1728	// which should not fail.
1729	//
1730	// Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
1731	// --------  ------  ------- ---- ---------- ----- --------  ----
1732	//        0  Defl:N        2   0% 12-01-2022 16:50 00000000  META-INF/
1733	//       60  Defl:N       59   2% 12-01-2022 16:50 af937e93  META-INF/MANIFEST.MF
1734	// --------          -------  ---                            -------
1735	//       60               61  -2%                            2 files
1736	data := []byte{
1737		0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08,
1738		0x08, 0x00, 0x49, 0x86, 0x81, 0x55, 0x00, 0x00,
1739		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1740		0x00, 0x00, 0x09, 0x00, 0x04, 0x00, 0x4d, 0x45,
1741		0x54, 0x41, 0x2d, 0x49, 0x4e, 0x46, 0x2f, 0xfe,
1742		0xca, 0x00, 0x00, 0x03, 0x00, 0x50, 0x4b, 0x07,
1743		0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
1744		0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03,
1745		0x04, 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x49,
1746		0x86, 0x81, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
1747		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
1748		0x00, 0x00, 0x00, 0x4d, 0x45, 0x54, 0x41, 0x2d,
1749		0x49, 0x4e, 0x46, 0x2f, 0x4d, 0x41, 0x4e, 0x49,
1750		0x46, 0x45, 0x53, 0x54, 0x2e, 0x4d, 0x46, 0xf3,
1751		0x4d, 0xcc, 0xcb, 0x4c, 0x4b, 0x2d, 0x2e, 0xd1,
1752		0x0d, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0xb3,
1753		0x52, 0x30, 0xd4, 0x33, 0xe0, 0xe5, 0x72, 0x2e,
1754		0x4a, 0x4d, 0x2c, 0x49, 0x4d, 0xd1, 0x75, 0xaa,
1755		0x04, 0x0a, 0x00, 0x45, 0xf4, 0x0c, 0x8d, 0x15,
1756		0x34, 0xdc, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0x15,
1757		0x3c, 0xf3, 0x92, 0xf5, 0x34, 0x79, 0xb9, 0x78,
1758		0xb9, 0x00, 0x50, 0x4b, 0x07, 0x08, 0x93, 0x7e,
1759		0x93, 0xaf, 0x3b, 0x00, 0x00, 0x00, 0x3c, 0x00,
1760		0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00,
1761		0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x49, 0x86,
1762		0x81, 0x55, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1763		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
1764		0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766		0x4d, 0x45, 0x54, 0x41, 0x2d, 0x49, 0x4e, 0x46,
1767		0x2f, 0xfe, 0xca, 0x00, 0x00, 0x50, 0x4b, 0x01,
1768		0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x08, 0x08,
1769		0x00, 0x49, 0x86, 0x81, 0x55, 0x93, 0x7e, 0x93,
1770		0xaf, 0x3b, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00,
1771		0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1772		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d,
1773		0x00, 0x00, 0x00, 0x4d, 0x45, 0x54, 0x41, 0x2d,
1774		0x49, 0x4e, 0x46, 0x2f, 0x4d, 0x41, 0x4e, 0x49,
1775		0x46, 0x45, 0x53, 0x54, 0x2e, 0x4d, 0x46, 0x50,
1776		0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x02,
1777		0x00, 0x02, 0x00, 0x7d, 0x00, 0x00, 0x00, 0xba,
1778		0x00, 0x00, 0x00, 0x00, 0x00,
1779	}
1780	r, err := NewReader(bytes.NewReader(data), int64(len(data)))
1781	if err != nil {
1782		t.Fatalf("unexpected error: %v", err)
1783	}
1784	for _, f := range r.File {
1785		r, err := f.Open()
1786		if err != nil {
1787			t.Fatalf("unexpected error: %v", err)
1788		}
1789		if _, err := io.Copy(io.Discard, r); err != nil {
1790			t.Fatalf("unexpected error: %v", err)
1791		}
1792	}
1793}
1794
1795func TestBaseOffsetPlusOverflow(t *testing.T) {
1796	// directoryOffset > maxInt64 && size-directoryOffset < 0
1797	data := []byte{
1798		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1799		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1800		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1801		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1802		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1803		0xff, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1804		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1805		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1806		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1807		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1808		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1809		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1810		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1811		0x20, 0x20, 0x20, 0x50, 0x4b, 0x06, 0x06, 0x20,
1812		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1813		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1814		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1815		0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1816		0x20, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x00,
1817		0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00,
1818		0x00, 0x00, 0x80, 0x50, 0x4b, 0x06, 0x07, 0x00,
1819		0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00,
1820		0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50,
1821		0x4b, 0x05, 0x06, 0x20, 0x20, 0x20, 0x20, 0xff,
1822		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1823		0xff, 0xff, 0xff, 0x20, 0x00,
1824	}
1825	defer func() {
1826		if r := recover(); r != nil {
1827			t.Fatalf("NewReader panicked: %s", r)
1828		}
1829	}()
1830	// Previously, this would trigger a panic as we attempt to read from
1831	// an io.SectionReader which would access a slice at a negative offset
1832	// as the section reader offset & size were < 0.
1833	NewReader(bytes.NewReader(data), int64(len(data))+1875)
1834}
1835