1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.compress.archivers.zip;
19
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import java.io.BufferedInputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.EOFException;
32 import java.io.File;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.nio.channels.Channels;
37 import java.nio.channels.SeekableByteChannel;
38 import java.nio.charset.StandardCharsets;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.nio.file.Paths;
42 import java.time.Instant;
43 import java.util.zip.ZipEntry;
44 import java.util.zip.ZipException;
45
46 import org.apache.commons.compress.AbstractTest;
47 import org.apache.commons.compress.archivers.ArchiveEntry;
48 import org.apache.commons.compress.archivers.ArchiveInputStream;
49 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
50 import org.apache.commons.compress.utils.ByteUtils;
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.lang3.ArrayFill;
53 import org.junit.jupiter.api.Test;
54 import org.junit.jupiter.params.ParameterizedTest;
55 import org.junit.jupiter.params.provider.ValueSource;
56
57 public class ZipArchiveInputStreamTest extends AbstractTest {
58
59 private static void nameSource(final String archive, final String entry, int entryNo, final ZipArchiveEntry.NameSource expected) throws Exception {
60 try (ZipArchiveInputStream zis = new ZipArchiveInputStream(Files.newInputStream(getFile(archive).toPath()))) {
61 ZipArchiveEntry ze;
62 do {
63 ze = zis.getNextZipEntry();
64 } while (--entryNo > 0);
65 assertEquals(entry, ze.getName());
66 assertEquals(expected, ze.getNameSource());
67 }
68 }
69
70 private static void nameSource(final String archive, final String entry, final ZipArchiveEntry.NameSource expected) throws Exception {
71 nameSource(archive, entry, 1, expected);
72 }
73
74 private static byte[] readEntry(final ZipArchiveInputStream zip, final ZipArchiveEntry zae) throws IOException {
75 final int len = (int) zae.getSize();
76 final byte[] buff = new byte[len];
77 zip.read(buff, 0, len);
78
79 return buff;
80 }
81
82 private void extractZipInputStream(final ZipArchiveInputStream inputStream) throws IOException {
83 ZipArchiveEntry zae = inputStream.getNextZipEntry();
84 while (zae != null) {
85 if (zae.getName().endsWith(".zip")) {
86 try (ZipArchiveInputStream innerInputStream = new ZipArchiveInputStream(inputStream)) {
87 extractZipInputStream(innerInputStream);
88 }
89 }
90 zae = inputStream.getNextZipEntry();
91 }
92 }
93
94
95
96
97
98
99
100 private InputStream forgeZipInputStream() throws IOException {
101 try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
102 ZipArchiveOutputStream zo = new ZipArchiveOutputStream(byteArrayOutputStream)) {
103
104 final ZipArchiveEntry entryA = new ZipArchiveEntry("foo");
105 entryA.setMethod(ZipEntry.STORED);
106 entryA.setSize(4);
107 entryA.setCrc(0xb63cfbcdL);
108
109 entryA.setTime(Instant.parse("2022-12-26T17:01:00Z").toEpochMilli());
110 zo.putArchiveEntry(entryA);
111 zo.write(new byte[] { 1, 2, 3, 4 });
112 zo.closeArchiveEntry();
113 zo.close();
114
115 final byte[] zipContent = byteArrayOutputStream.toByteArray();
116 final byte[] zipContentWithDataDescriptor = new byte[zipContent.length + 12];
117 System.arraycopy(zipContent, 0, zipContentWithDataDescriptor, 0, 37);
118
119 zipContentWithDataDescriptor[6] = 8;
120
121
122 System.arraycopy(zipContent, 14, zipContentWithDataDescriptor, 37, 12);
123
124
125 System.arraycopy(zipContent, 37, zipContentWithDataDescriptor, 49, zipContent.length - 37);
126
127 return new ByteArrayInputStream(zipContentWithDataDescriptor);
128 }
129 }
130
131 private void fuzzingTest(final int[] bytes) throws Exception {
132 final int len = bytes.length;
133 final byte[] input = new byte[len];
134 for (int i = 0; i < len; i++) {
135 input[i] = (byte) bytes[i];
136 }
137 try (ArchiveInputStream<?> ais = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("zip", new ByteArrayInputStream(input))) {
138 ais.getNextEntry();
139 IOUtils.toByteArray(ais);
140 }
141 }
142
143 private void getAllZipEntries(final ZipArchiveInputStream zipInputStream) throws IOException {
144 while (zipInputStream.getNextZipEntry() != null) {
145
146 }
147 }
148
149 private void multiByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception {
150 final byte[] buf = new byte[2];
151 try (InputStream in = newInputStream("bla.zip");
152 ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
153 assertNotNull(archive.getNextEntry());
154 IOUtils.toByteArray(archive);
155 assertEquals(-1, archive.read(buf));
156 assertEquals(-1, archive.read(buf));
157 }
158 }
159
160 private void singleByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception {
161 try (InputStream in = Files.newInputStream(file.toPath());
162 ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
163 assertNotNull(archive.getNextEntry());
164 IOUtils.toByteArray(archive);
165 assertEquals(-1, archive.read());
166 assertEquals(-1, archive.read());
167 }
168 }
169
170 @Test
171 public void testGetFirstEntryEmptyZip() throws IOException {
172 try (ZipArchiveInputStream zin = new ZipArchiveInputStream(new ByteArrayInputStream(ByteUtils.EMPTY_BYTE_ARRAY))) {
173 final ZipArchiveEntry entry = zin.getNextEntry();
174 assertNull(entry);
175 }
176 }
177
178
179
180
181 @Test
182 public void testMessageWithCorruptFileName() throws Exception {
183 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("COMPRESS-351.zip"))) {
184 final EOFException ex = assertThrows(EOFException.class, () -> {
185 ZipArchiveEntry ze = in.getNextZipEntry();
186 while (ze != null) {
187 ze = in.getNextZipEntry();
188 }
189 }, "expected EOFException");
190 final String m = ex.getMessage();
191 assertTrue(m.startsWith("Truncated ZIP entry: ?2016"));
192 }
193 }
194
195 @Test
196 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
197 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
198 }
199
200 @Test
201 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
202 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
203 }
204
205 @Test
206 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
207 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
208 }
209
210 @Test
211 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
212 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
213 }
214
215 @Test
216 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
217 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
218 }
219
220 @Test
221 public void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
222 multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
223 }
224
225 @Test
226 public void testMultiByteReadThrowsAtEofForCorruptedStoredEntry() throws Exception {
227 final byte[] content = readAllBytes("COMPRESS-264.zip");
228
229 for (int i = 17; i < 26; i++) {
230 content[i] = (byte) 0xff;
231 }
232 final byte[] buf = new byte[2];
233 try (ByteArrayInputStream in = new ByteArrayInputStream(content);
234 ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
235 assertNotNull(archive.getNextEntry());
236 final IOException ex1 = assertThrows(IOException.class, () -> IOUtils.toByteArray(archive), "expected exception");
237 assertEquals("Truncated ZIP file", ex1.getMessage());
238 final IOException ex2 = assertThrows(IOException.class, () -> archive.read(buf), "expected exception");
239 assertEquals("Truncated ZIP file", ex2.getMessage());
240 final IOException ex3 = assertThrows(IOException.class, () -> archive.read(buf), "expected exception");
241 assertEquals("Truncated ZIP file", ex3.getMessage());
242 }
243 }
244
245 @Test
246 public void testNameSourceDefaultsToName() throws Exception {
247 nameSource("bla.zip", "test1.xml", ZipArchiveEntry.NameSource.NAME);
248 }
249
250 @Test
251 public void testNameSourceIsSetToEFS() throws Exception {
252 nameSource("utf8-7zip-test.zip", "\u20AC_for_Dollar.txt", 3, ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
253 }
254
255 @Test
256 public void testNameSourceIsSetToUnicodeExtraField() throws Exception {
257 nameSource("utf8-winzip-test.zip", "\u20AC_for_Dollar.txt", ZipArchiveEntry.NameSource.UNICODE_EXTRA_FIELD);
258 }
259
260
261
262
263 @Test
264 public void testOffsets() throws Exception {
265
266 try (InputStream archiveStream = ZipArchiveInputStream.class.getResourceAsStream("/mixed.zip");
267 ZipArchiveInputStream zipStream = new ZipArchiveInputStream(archiveStream)) {
268 final ZipArchiveEntry inflatedEntry = zipStream.getNextZipEntry();
269 assertEquals("inflated.txt", inflatedEntry.getName());
270 assertEquals(0x0000, inflatedEntry.getLocalHeaderOffset());
271 assertEquals(0x0046, inflatedEntry.getDataOffset());
272 final ZipArchiveEntry storedEntry = zipStream.getNextZipEntry();
273 assertEquals("stored.txt", storedEntry.getName());
274 assertEquals(0x5892, storedEntry.getLocalHeaderOffset());
275 assertEquals(0x58d6, storedEntry.getDataOffset());
276 assertNull(zipStream.getNextZipEntry());
277 }
278 }
279
280 @Test
281 public void testProperlyMarksEntriesAsUnreadableIfUncompressedSizeIsUnknown() throws Exception {
282
283 try (ZipArchiveInputStream zis = new ZipArchiveInputStream(new ByteArrayInputStream(ByteUtils.EMPTY_BYTE_ARRAY))) {
284 final ZipArchiveEntry e = new ZipArchiveEntry("test");
285 e.setMethod(ZipMethod.DEFLATED.getCode());
286 assertTrue(zis.canReadEntryData(e));
287 e.setMethod(ZipMethod.ENHANCED_DEFLATED.getCode());
288 assertTrue(zis.canReadEntryData(e));
289 e.setMethod(ZipMethod.BZIP2.getCode());
290 assertFalse(zis.canReadEntryData(e));
291 }
292 }
293
294 @Test
295 public void testProperlyReadsStoredEntries() throws IOException {
296 try (InputStream fs = newInputStream("bla-stored.zip");
297 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs)) {
298 ZipArchiveEntry e = archive.getNextZipEntry();
299 assertNotNull(e);
300 assertEquals("test1.xml", e.getName());
301 assertEquals(610, e.getCompressedSize());
302 assertEquals(610, e.getSize());
303 byte[] data = IOUtils.toByteArray(archive);
304 assertEquals(610, data.length);
305 e = archive.getNextZipEntry();
306 assertNotNull(e);
307 assertEquals("test2.xml", e.getName());
308 assertEquals(82, e.getCompressedSize());
309 assertEquals(82, e.getSize());
310 data = IOUtils.toByteArray(archive);
311 assertEquals(82, data.length);
312 assertNull(archive.getNextEntry());
313 }
314 }
315
316 @Test
317 public void testProperlyReadsStoredEntryWithDataDescriptorWithoutSignature() throws IOException {
318 try (InputStream fs = newInputStream("bla-stored-dd-nosig.zip");
319 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs, StandardCharsets.UTF_8.name(), true, true)) {
320 final ZipArchiveEntry e = archive.getNextZipEntry();
321 assertNotNull(e);
322 assertEquals("test1.xml", e.getName());
323 assertEquals(-1, e.getCompressedSize());
324 assertEquals(-1, e.getSize());
325 final byte[] data = IOUtils.toByteArray(archive);
326 assertEquals(610, data.length);
327 assertEquals(610, e.getCompressedSize());
328 assertEquals(610, e.getSize());
329 }
330 }
331
332 @Test
333 public void testProperlyReadsStoredEntryWithDataDescriptorWithSignature() throws IOException {
334 try (InputStream fs = newInputStream("bla-stored-dd.zip");
335 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs, StandardCharsets.UTF_8.name(), true, true)) {
336 final ZipArchiveEntry e = archive.getNextZipEntry();
337 assertNotNull(e);
338 assertEquals("test1.xml", e.getName());
339 assertEquals(-1, e.getCompressedSize());
340 assertEquals(-1, e.getSize());
341 final byte[] data = IOUtils.toByteArray(archive);
342 assertEquals(610, data.length);
343 assertEquals(610, e.getCompressedSize());
344 assertEquals(610, e.getSize());
345 }
346 }
347
348
349
350
351 @Test
352 public void testProperUseOfInflater() throws Exception {
353 try (ZipFile zf = ZipFile.builder().setFile(getFile("COMPRESS-189.zip")).get()) {
354 final ZipArchiveEntry zae = zf.getEntry("USD0558682-20080101.ZIP");
355 try (ZipArchiveInputStream in = new ZipArchiveInputStream(new BufferedInputStream(zf.getInputStream(zae)))) {
356 ZipArchiveEntry innerEntry;
357 while ((innerEntry = in.getNextZipEntry()) != null) {
358 if (innerEntry.getName().endsWith("XML")) {
359 assertTrue(0 < in.read());
360 }
361 }
362 }
363 }
364 }
365
366
367
368
369 @Test
370 public void testReadDeflate64CompressedStream() throws Exception {
371 final byte[] orig = readAllBytes("COMPRESS-380/COMPRESS-380-input");
372 final File archive = getFile("COMPRESS-380/COMPRESS-380.zip");
373 try (ZipArchiveInputStream zin = new ZipArchiveInputStream(Files.newInputStream(archive.toPath()))) {
374 assertNotNull(zin.getNextZipEntry());
375 final byte[] fromZip = IOUtils.toByteArray(zin);
376 assertArrayEquals(orig, fromZip);
377 }
378 }
379
380 @Test
381 public void testReadDeflate64CompressedStreamWithDataDescriptor() throws Exception {
382
383 final File archive = getFile("COMPRESS-380/COMPRESS-380-dd.zip");
384 try (ZipArchiveInputStream zin = new ZipArchiveInputStream(Files.newInputStream(archive.toPath()))) {
385 final ZipArchiveEntry e = zin.getNextZipEntry();
386 assertEquals(-1, e.getSize());
387 assertEquals(ZipMethod.ENHANCED_DEFLATED.getCode(), e.getMethod());
388 final byte[] fromZip = IOUtils.toByteArray(zin);
389 final byte[] expected = { 'M', 'a', 'n', 'i', 'f', 'e', 's', 't', '-', 'V', 'e', 'r', 's', 'i', 'o', 'n', ':', ' ', '1', '.', '0', '\r', '\n', '\r',
390 '\n' };
391 assertArrayEquals(expected, fromZip);
392 zin.getNextZipEntry();
393 assertEquals(25, e.getSize());
394 }
395 }
396
397
398
399
400 @Test
401 public void testReadingOfFirstStoredEntry() throws Exception {
402
403 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("COMPRESS-264.zip"))) {
404 final ZipArchiveEntry ze = in.getNextZipEntry();
405 assertEquals(5, ze.getSize());
406 assertArrayEquals(new byte[] { 'd', 'a', 't', 'a', '\n' }, IOUtils.toByteArray(in));
407 }
408 }
409
410 @Test
411 public void testRejectsStoredEntriesWithDataDescriptorByDefault() throws IOException {
412 try (InputStream fs = newInputStream("bla-stored-dd.zip");
413 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs)) {
414 final ZipArchiveEntry e = archive.getNextZipEntry();
415 assertNotNull(e);
416 assertEquals("test1.xml", e.getName());
417 assertEquals(-1, e.getCompressedSize());
418 assertEquals(-1, e.getSize());
419 assertThrows(UnsupportedZipFeatureException.class, () -> IOUtils.toByteArray(archive));
420 }
421 }
422
423 @Test
424 public void testShouldConsumeArchiveCompletely() throws Exception {
425 try (InputStream is = ZipArchiveInputStreamTest.class.getResourceAsStream("/archive_with_trailer.zip");
426 ZipArchiveInputStream zip = new ZipArchiveInputStream(is)) {
427 getAllZipEntries(zip);
428 final byte[] expected = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n' };
429 final byte[] actual = new byte[expected.length];
430 is.read(actual);
431 assertArrayEquals(expected, actual);
432 }
433 }
434
435
436
437
438 @Test
439 public void testShouldReadNestedZip() throws IOException {
440 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("COMPRESS-219.zip"))) {
441 extractZipInputStream(in);
442 }
443 }
444
445 @Test
446 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
447 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
448 }
449
450 @Test
451 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
452 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
453 }
454
455 @Test
456 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
457 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
458 }
459
460 @Test
461 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
462 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
463 }
464
465 @Test
466 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
467 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
468 }
469
470 @Test
471 public void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
472 singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
473 }
474
475 @Test
476 public void testSingleByteReadThrowsAtEofForCorruptedStoredEntry() throws Exception {
477 final byte[] content = readAllBytes("COMPRESS-264.zip");
478
479 for (int i = 17; i < 26; i++) {
480 content[i] = (byte) 0xff;
481 }
482 try (ByteArrayInputStream in = new ByteArrayInputStream(content);
483 ZipArchiveInputStream archive = new ZipArchiveInputStream(in)) {
484 assertNotNull(archive.getNextEntry());
485 final IOException ex1 = assertThrows(IOException.class, () -> IOUtils.toByteArray(archive), "expected exception");
486 assertEquals("Truncated ZIP file", ex1.getMessage());
487 final IOException ex2 = assertThrows(IOException.class, archive::read, "expected exception");
488 assertEquals("Truncated ZIP file", ex2.getMessage());
489 final IOException ex3 = assertThrows(IOException.class, archive::read, "expected exception");
490 assertEquals("Truncated ZIP file", ex3.getMessage());
491 }
492 }
493
494 @Test
495 public void testSplitZipCreatedByWinrar() throws IOException {
496 final File lastFile = getFile("COMPRESS-477/split_zip_created_by_winrar/split_zip_created_by_winrar.zip");
497 try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile);
498 InputStream inputStream = Channels.newInputStream(channel);
499 ZipArchiveInputStream splitInputStream = new ZipArchiveInputStream(inputStream, StandardCharsets.UTF_8.name(), true, false, true)) {
500
501 final File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_winrar/zip_to_compare_created_by_winrar.zip");
502 try (ZipArchiveInputStream inputStreamToCompare = new ZipArchiveInputStream(Files.newInputStream(fileToCompare.toPath()),
503 StandardCharsets.UTF_8.name(), true, false, true)) {
504
505 ArchiveEntry entry;
506 while ((entry = splitInputStream.getNextEntry()) != null && inputStreamToCompare.getNextEntry() != null) {
507 if (entry.isDirectory()) {
508 continue;
509 }
510 assertArrayEquals(IOUtils.toByteArray(splitInputStream), IOUtils.toByteArray(inputStreamToCompare));
511 }
512 }
513 }
514 }
515
516 @Test
517 public void testSplitZipCreatedByZip() throws IOException {
518 final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip.zip");
519 try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile);
520 InputStream inputStream = Channels.newInputStream(channel);
521 ZipArchiveInputStream splitInputStream = new ZipArchiveInputStream(inputStream, StandardCharsets.UTF_8.name(), true, false, true)) {
522
523 final Path fileToCompare = getPath("COMPRESS-477/split_zip_created_by_zip/zip_to_compare_created_by_zip.zip");
524 try (ZipArchiveInputStream inputStreamToCompare = new ZipArchiveInputStream(Files.newInputStream(fileToCompare), StandardCharsets.UTF_8.name(),
525 true, false, true)) {
526
527 ArchiveEntry entry;
528 while ((entry = splitInputStream.getNextEntry()) != null && inputStreamToCompare.getNextEntry() != null) {
529 if (entry.isDirectory()) {
530 continue;
531 }
532 assertArrayEquals(IOUtils.toByteArray(splitInputStream), IOUtils.toByteArray(inputStreamToCompare));
533 }
534 }
535 }
536 }
537
538 @Test
539 public void testSplitZipCreatedByZipOfZip64() throws IOException {
540 final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip_zip64.zip");
541 try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile);
542 InputStream inputStream = Channels.newInputStream(channel);
543 ZipArchiveInputStream splitInputStream = new ZipArchiveInputStream(inputStream, StandardCharsets.UTF_8.name(), true, false, true)) {
544
545 final Path fileToCompare = getPath("COMPRESS-477/split_zip_created_by_zip/zip_to_compare_created_by_zip_zip64.zip");
546 try (ZipArchiveInputStream inputStreamToCompare = new ZipArchiveInputStream(Files.newInputStream(fileToCompare), StandardCharsets.UTF_8.name(),
547 true, false, true)) {
548
549 ArchiveEntry entry;
550 while ((entry = splitInputStream.getNextEntry()) != null && inputStreamToCompare.getNextEntry() != null) {
551 if (entry.isDirectory()) {
552 continue;
553 }
554 assertArrayEquals(IOUtils.toByteArray(splitInputStream), IOUtils.toByteArray(inputStreamToCompare));
555 }
556 }
557 }
558 }
559
560 @Test
561 public void testSplitZipCreatedByZipThrowsException() throws IOException {
562 final File zipSplitFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip.z01");
563 try (ZipArchiveInputStream inputStream = new ZipArchiveInputStream(Files.newInputStream(zipSplitFile.toPath()), StandardCharsets.UTF_8.name(), true,
564 false, true)) {
565
566 assertThrows(EOFException.class, () -> {
567 ArchiveEntry entry = inputStream.getNextEntry();
568 while (entry != null) {
569 entry = inputStream.getNextEntry();
570 }
571 });
572 }
573 }
574
575
576
577
578 @Test
579 public void testThrowOnInvalidEntry() throws Exception {
580 try (ZipArchiveInputStream zip = new ZipArchiveInputStream(ZipArchiveInputStreamTest.class.getResourceAsStream("/invalid-zip.zip"))) {
581 final ZipException expected = assertThrows(ZipException.class, zip::getNextZipEntry, "IOException expected");
582 assertTrue(expected.getMessage().contains("Cannot find zip signature"));
583 }
584 }
585
586 @Test
587 public void testThrowsIfStoredDDIsDifferentFromLengthRead() throws IOException {
588 try (InputStream fs = newInputStream("bla-stored-dd-contradicts-actualsize.zip");
589 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs, StandardCharsets.UTF_8.name(), true, true)) {
590 final ZipArchiveEntry e = archive.getNextZipEntry();
591 assertNotNull(e);
592 assertEquals("test1.xml", e.getName());
593 assertEquals(-1, e.getCompressedSize());
594 assertEquals(-1, e.getSize());
595 assertThrows(ZipException.class, () -> IOUtils.toByteArray(archive));
596 }
597 }
598
599 @Test
600 public void testThrowsIfStoredDDIsInconsistent() throws IOException {
601 try (InputStream fs = newInputStream("bla-stored-dd-sizes-differ.zip");
602 ZipArchiveInputStream archive = new ZipArchiveInputStream(fs, StandardCharsets.UTF_8.name(), true, true)) {
603 final ZipArchiveEntry e = archive.getNextZipEntry();
604 assertNotNull(e);
605 assertEquals("test1.xml", e.getName());
606 assertEquals(-1, e.getCompressedSize());
607 assertEquals(-1, e.getSize());
608 assertThrows(ZipException.class, () -> IOUtils.toByteArray(archive));
609 }
610 }
611
612
613
614
615 @Test
616 public void testThrowsIfThereIsNoEocd() {
617 assertThrows(IOException.class, () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
618 0x00, 0x43, 0xbe, 0x00, 0x00, 0x00, 0xb7, 0xe8, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }));
619 }
620
621
622
623
624 @Test
625 public void testThrowsIfZip64ExtraCouldNotBeUnderstood() {
626 assertThrows(IOException.class,
627 () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x2e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x84, 0xb6, 0xba, 0x46, 0x72, 0xb6, 0xfe, 0x77, 0x63,
628 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x62, 0x62, 0x62, 0x01, 0x00, 0x09, 0x00, 0x03, 0xe7, 0xce, 0x64,
629 0x55, 0xf3, 0xce, 0x64, 0x55, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x5c, 0xf9, 0x01, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00 }));
630 }
631
632 @Test
633 public void testThrowsIOExceptionIfThereIsCorruptedZip64Extra() throws IOException {
634 try (InputStream fis = newInputStream("COMPRESS-546.zip");
635 ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(fis)) {
636 assertThrows(IOException.class, () -> getAllZipEntries(zipInputStream));
637 }
638 }
639
640 @Test
641 public void testUnshrinkEntry() throws Exception {
642 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("SHRUNK.ZIP"))) {
643 ZipArchiveEntry entry = in.getNextZipEntry();
644 assertEquals(ZipMethod.UNSHRINKING.getCode(), entry.getMethod(), "method");
645 assertTrue(in.canReadEntryData(entry));
646
647 try (InputStream original = newInputStream("test1.xml")) {
648 try {
649 assertArrayEquals(IOUtils.toByteArray(original), IOUtils.toByteArray(in));
650 } finally {
651 original.close();
652 }
653
654 entry = in.getNextZipEntry();
655 assertEquals(ZipMethod.UNSHRINKING.getCode(), entry.getMethod(), "method");
656 assertTrue(in.canReadEntryData(entry));
657 }
658
659 assertArrayEquals(readAllBytes("test2.xml"), IOUtils.toByteArray(in));
660 }
661 }
662
663 @Test
664 public void testUnzipBZip2CompressedEntry() throws Exception {
665
666 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("bzip2-zip.zip"))) {
667 final ZipArchiveEntry ze = in.getNextZipEntry();
668 assertEquals(42, ze.getSize());
669 final byte[] expected = ArrayFill.fill(new byte[42], (byte) 'a');
670 assertArrayEquals(expected, IOUtils.toByteArray(in));
671 }
672 }
673
674
675
676
677 @Test
678 public void testWinzipBackSlashWorkaround() throws Exception {
679 try (ZipArchiveInputStream in = new ZipArchiveInputStream(newInputStream("test-winzip.zip"))) {
680 ZipArchiveEntry zae = in.getNextZipEntry();
681 zae = in.getNextZipEntry();
682 zae = in.getNextZipEntry();
683 assertEquals("\u00e4/", zae.getName());
684 }
685 }
686
687
688
689
690 @Test
691 public void testWithBytesAfterData() throws Exception {
692 final int expectedNumEntries = 2;
693 try (InputStream is = ZipArchiveInputStreamTest.class.getResourceAsStream("/archive_with_bytes_after_data.zip");
694 ZipArchiveInputStream zip = new ZipArchiveInputStream(is)) {
695 int actualNumEntries = 0;
696 ZipArchiveEntry zae = zip.getNextZipEntry();
697 while (zae != null) {
698 actualNumEntries++;
699 readEntry(zip, zae);
700 zae = zip.getNextZipEntry();
701 }
702 assertEquals(expectedNumEntries, actualNumEntries);
703 }
704 }
705
706 @Test
707 public void testZipUsingStoredWithDDAndNoDDSignature() throws IOException {
708 try (InputStream inputStream = forgeZipInputStream();
709 ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(inputStream, StandardCharsets.UTF_8.name(), true, true)) {
710 getAllZipEntries(zipInputStream);
711 }
712 }
713
714 @Test
715 public void testZipWithBadExtraFields() throws IOException {
716 try (InputStream fis = newInputStream("COMPRESS-548.zip");
717 ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(fis)) {
718 getAllZipEntries(zipInputStream);
719 }
720 }
721
722 @Test
723 public void testZipWithLongerBeginningGarbage() throws IOException {
724 final Path path = createTempPath("preamble", ".zip");
725
726 try (OutputStream fos = Files.newOutputStream(path)) {
727 fos.write("#!/usr/bin/env some-program with quite a few arguments to make it longer than the local header\n".getBytes(StandardCharsets.UTF_8));
728 try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
729 final ZipArchiveEntry entry = new ZipArchiveEntry("file-1.txt");
730 entry.setMethod(ZipEntry.DEFLATED);
731 zos.putArchiveEntry(entry);
732 zos.write("entry-content\n".getBytes(StandardCharsets.UTF_8));
733 zos.closeArchiveEntry();
734 }
735 }
736
737 try (InputStream is = Files.newInputStream(path);
738 ZipArchiveInputStream zis = new ZipArchiveInputStream(is)) {
739 final ZipArchiveEntry entry = zis.getNextEntry();
740 assertEquals("file-1.txt", entry.getName());
741 final byte[] content = IOUtils.toByteArray(zis);
742 assertArrayEquals("entry-content\n".getBytes(StandardCharsets.UTF_8), content);
743 }
744 }
745
746 @Test
747 public void testZipWithShortBeginningGarbage() throws IOException {
748 final Path path = createTempPath("preamble", ".zip");
749
750 try (OutputStream fos = Files.newOutputStream(path)) {
751 fos.write("#!/usr/bin/unzip\n".getBytes(StandardCharsets.UTF_8));
752 try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
753 final ZipArchiveEntry entry = new ZipArchiveEntry("file-1.txt");
754 entry.setMethod(ZipEntry.DEFLATED);
755 zos.putArchiveEntry(entry);
756 zos.write("entry-content\n".getBytes(StandardCharsets.UTF_8));
757 zos.closeArchiveEntry();
758 }
759 }
760
761 try (InputStream is = Files.newInputStream(path);
762 ZipArchiveInputStream zis = new ZipArchiveInputStream(is)) {
763 final ZipArchiveEntry entry = zis.getNextEntry();
764 assertEquals("file-1.txt", entry.getName());
765 final byte[] content = IOUtils.toByteArray(zis);
766 assertArrayEquals("entry-content\n".getBytes(StandardCharsets.UTF_8), content);
767 }
768 }
769
770 @ParameterizedTest
771 @ValueSource(booleans = { true, false })
772 public void zipInputStream(final boolean allowStoredEntriesWithDataDescriptor) {
773 try (ZipArchiveInputStream zIn = new ZipArchiveInputStream(Files.newInputStream(Paths.get("src/test/resources/COMPRESS-647/test.zip")),
774 StandardCharsets.UTF_8.name(), false, allowStoredEntriesWithDataDescriptor)) {
775 ZipArchiveEntry zae = zIn.getNextEntry();
776 while (zae != null) {
777 zae = zIn.getNextEntry();
778 }
779 } catch (final IOException e) {
780
781 }
782 }
783
784 }