View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.compressors.deflate64;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNull;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  import static org.mockito.Mockito.times;
23  
24  import java.io.BufferedReader;
25  import java.io.ByteArrayInputStream;
26  import java.io.EOFException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.InputStreamReader;
30  
31  import org.apache.commons.compress.archivers.ArchiveException;
32  import org.apache.commons.compress.archivers.ArchiveInputStream;
33  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
34  import org.apache.commons.compress.compressors.CompressorStreamFactory;
35  import org.apache.commons.io.IOUtils;
36  import org.junit.jupiter.api.Test;
37  import org.junit.jupiter.api.extension.ExtendWith;
38  import org.mockito.Mock;
39  import org.mockito.Mockito;
40  import org.mockito.junit.jupiter.MockitoExtension;
41  
42  @ExtendWith(MockitoExtension.class)
43  public class Deflate64CompressorInputStreamTest {
44      private final HuffmanDecoder nullDecoder = null;
45  
46      @Mock
47      private HuffmanDecoder decoder;
48  
49      private void fuzzingTest(final int[] bytes) throws IOException, ArchiveException {
50          final int len = bytes.length;
51          final byte[] input = new byte[len];
52          for (int i = 0; i < len; i++) {
53              input[i] = (byte) bytes[i];
54          }
55          try (ArchiveInputStream<?> ais = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("zip", new ByteArrayInputStream(input))) {
56              ais.getNextEntry();
57              IOUtils.toByteArray(ais);
58          }
59      }
60  
61      @Test
62      public void testCloseCallsDecoder() throws Exception {
63  
64          try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder)) {
65              // empty
66          }
67  
68          Mockito.verify(decoder, times(1)).close();
69      }
70  
71      @Test
72      public void testCloseIsDelegatedJustOnce() throws Exception {
73  
74          try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder)) {
75              input.close();
76          }
77  
78          Mockito.verify(decoder, times(1)).close();
79      }
80  
81      @Test
82      public void testDelegatesAvailable() throws Exception {
83          Mockito.when(decoder.available()).thenReturn(1024);
84  
85          try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(decoder)) {
86              assertEquals(1024, input.available());
87          }
88      }
89  
90      @Test
91      public void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
92          final byte[] buf = new byte[2];
93          try (Deflate64CompressorInputStream in = new Deflate64CompressorInputStream(nullDecoder)) {
94              IOUtils.toByteArray(in);
95              assertEquals(-1, in.read(buf));
96              assertEquals(-1, in.read(buf));
97          }
98      }
99  
100     @Test
101     public void testProperSizeWhenClosed() throws Exception {
102         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder)) {
103             assertEquals(0, input.available());
104         }
105     }
106 
107     @Test
108     public void testReadWhenClosed() throws Exception {
109         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(nullDecoder)) {
110             assertEquals(-1, input.read());
111             assertEquals(-1, input.read(new byte[1]));
112             assertEquals(-1, input.read(new byte[1], 0, 1));
113         }
114     }
115 
116     /**
117      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-521">COMPRESS-521</a>
118      */
119     @Test
120     public void testShouldThrowIOExceptionInsteadOfRuntimeExceptionCOMPRESS521() {
121         assertThrows(IOException.class,
122                 () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x2e, 0x00, 0xb6, 0x00, 0x09, 0x00, 0x84, 0xb6, 0xba, 0x46, 0x72, 0x00, 0xfe, 0x77, 0x63,
123                         0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x62, 0x62, 0x62, 0x55, 0x54, 0x0c, 0x00, 0x03, 0xe7, 0xce, 0x64,
124                         0x55, 0xf3, 0xce, 0x65, 0x55, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x5c, 0xf9, 0x01, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x42, 0x5a,
125                         0x68, 0x34 }));
126     }
127 
128     /**
129      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-522">COMPRESS-522</a>
130      */
131     @Test
132     public void testShouldThrowIOExceptionInsteadOfRuntimeExceptionCOMPRESS522() {
133         assertThrows(IOException.class,
134                 () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x61, 0x4a, 0x84, 0x02, 0x40, 0x00, 0x01, 0x00, 0xff, 0xff }));
136     }
137 
138     /**
139      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-525">COMPRESS-525</a>
140      */
141     @Test
142     public void testShouldThrowIOExceptionInsteadOfRuntimeExceptionCOMPRESS525() {
143         assertThrows(IOException.class, () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x78, 0x00, 0x61, 0x4a, 0x04, 0x04, 0x00, 0x00, 0xff, 0xff, 0x50,
145                 0x53, 0x07, 0x08, 0x43, 0xbe, 0xb7, 0xe8, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08,
146                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62, 0x4a, 0x02,
147                 0x04, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x7f, 0x08, 0xf9, 0xef, 0xbe, 0x71, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03,
148                 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
149                 0x00, 0x00, 0x00, 0x63, 0x4a, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x6f, 0xdf }));
150     }
151 
152     /**
153      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-526">COMPRESS-526</a>
154      */
155     @Test
156     public void testShouldThrowIOExceptionInsteadOfRuntimeExceptionCOMPRESS526() {
157         assertThrows(IOException.class,
158                 () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x61, 0x4a, 0x04, 0x04, 0x00, 0x00, 0xff, 0xff, 0x50, 0x53, 0x07,
160                         0x08, 0x43, 0xbe, 0xb7, 0xe8, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08,
161                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x62,
162                         0x4a, 0x02, 0x04, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x7f, 0x08, 0xf9, 0xef, 0xbe, 0x71, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
163                         0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164                         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x4a, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x01,
165                         0xdf, 0xb9, 0x06, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08 }));
166     }
167 
168     /**
169      * @see <a href="https://issues.apache.org/jira/browse/COMPRESS-527">COMPRESS-527</a>
170      */
171     @Test
172     public void testShouldThrowIOExceptionInsteadOfRuntimeExceptionCOMPRESS527() {
173         assertThrows(IOException.class,
174                 () -> fuzzingTest(new int[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x84, 0xb6, 0xba, 0x46, 0x72, 0xb6, 0xfe, 0x77, 0x4a,
175                         0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x62, 0x62, 0x62, 0x55, 0x54, 0x09, 0x00, 0x03, 0xe7, 0xce, 0x64,
176                         0x55, 0xf3, 0xce, 0x64, 0x55, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x5c, 0xf9, 0x01, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x1d, 0x8b,
177                         0xc1, 0x0d, 0xc0, 0x30, 0x08, 0x03, 0xff, 0x99, 0xc2, 0xab, 0x81, 0x50, 0x1a, 0xa8, 0x44, 0x1e, 0x56, 0x30, 0x7f, 0x21, 0x1f, 0x5b,
178                         0x3e, 0x9d, 0x85, 0x6e }));
179     }
180 
181     @Test
182     public void testShouldThrowsEOFExceptionOnTruncatedStreams() throws IOException {
183         final byte[] data = { 1, 11, 0, -12, -1, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', };
184 
185         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data));
186                 BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
187             assertThrows(EOFException.class, () -> br.readLine());
188         }
189     }
190 
191     @Test
192     public void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
193         try (Deflate64CompressorInputStream in = new Deflate64CompressorInputStream(nullDecoder)) {
194             IOUtils.toByteArray(in);
195             assertEquals(-1, in.read());
196             assertEquals(-1, in.read());
197         }
198     }
199 
200     @Test
201     public void testStreamIgnoresExtraBytesAfterDeflatedInput() throws Exception {
202         final byte[] data = { 1, 11, 0, -12, -1, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', 'X' };
203 
204         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data));
205                 BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
206             assertEquals("Hello World", br.readLine());
207             assertNull(br.readLine());
208         }
209     }
210 
211     @Test
212     public void testUncompressedBlock() throws Exception {
213         final byte[] data = { 1, 11, 0, -12, -1, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
214 
215         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data));
216                 BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
217             assertEquals("Hello World", br.readLine());
218             assertNull(br.readLine());
219         }
220     }
221 
222     @Test
223     public void testUncompressedBlockAvailable() throws Exception {
224         final byte[] data = { 1, 11, 0, -12, -1, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
225 
226         try (Deflate64CompressorInputStream input = new Deflate64CompressorInputStream(new ByteArrayInputStream(data))) {
227             assertEquals('H', input.read());
228             assertEquals(10, input.available());
229         }
230     }
231 
232     @Test
233     public void testUncompressedBlockViaFactory() throws Exception {
234         final byte[] data = { 1, 11, 0, -12, -1, 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
235 
236         try (InputStream input = new CompressorStreamFactory().createCompressorInputStream(CompressorStreamFactory.DEFLATE64, new ByteArrayInputStream(data));
237                 BufferedReader br = new BufferedReader(new InputStreamReader(input))) {
238             assertEquals("Hello World", br.readLine());
239             assertNull(br.readLine());
240         }
241     }
242 }