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