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.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertThrows;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23
24 import java.util.zip.ZipException;
25
26 import org.junit.jupiter.api.BeforeEach;
27 import org.junit.jupiter.api.Test;
28
29
30
31
32 public class ExtraFieldUtilsTest implements UnixStat {
33
34 public static class AiobThrowingExtraField implements ZipExtraField {
35 static final int LENGTH = 4;
36
37 @Override
38 public byte[] getCentralDirectoryData() {
39 return getLocalFileDataData();
40 }
41
42 @Override
43 public ZipShort getCentralDirectoryLength() {
44 return getLocalFileDataLength();
45 }
46
47 @Override
48 public ZipShort getHeaderId() {
49 return AIOB_HEADER;
50 }
51
52 @Override
53 public byte[] getLocalFileDataData() {
54 return new byte[LENGTH];
55 }
56
57 @Override
58 public ZipShort getLocalFileDataLength() {
59 return new ZipShort(LENGTH);
60 }
61
62 @Override
63 public void parseFromCentralDirectoryData(final byte[] buffer, final int offset, final int length) {
64 parseFromLocalFileData(buffer, offset, length);
65 }
66
67 @Override
68 public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length) {
69 throw new ArrayIndexOutOfBoundsException();
70 }
71 }
72
73
74
75
76
77
78
79
80 static final ZipShort UNRECOGNIZED_HEADER = new ZipShort(0x5555);
81
82
83
84
85 static final ZipShort AIOB_HEADER = new ZipShort(0x1000);
86 private AsiExtraField a;
87 private UnrecognizedExtraField dummy;
88 private byte[] data;
89
90 private byte[] aLocal;
91
92 @BeforeEach
93 public void setUp() {
94 a = new AsiExtraField();
95 a.setMode(0755);
96 a.setDirectory(true);
97 dummy = new UnrecognizedExtraField();
98 dummy.setHeaderId(UNRECOGNIZED_HEADER);
99 dummy.setLocalFileDataData(new byte[] { 0 });
100 dummy.setCentralDirectoryData(new byte[] { 0 });
101
102 aLocal = a.getLocalFileDataData();
103 final byte[] dummyLocal = dummy.getLocalFileDataData();
104 data = new byte[4 + aLocal.length + 4 + dummyLocal.length];
105 System.arraycopy(a.getHeaderId().getBytes(), 0, data, 0, 2);
106 System.arraycopy(a.getLocalFileDataLength().getBytes(), 0, data, 2, 2);
107 System.arraycopy(aLocal, 0, data, 4, aLocal.length);
108 System.arraycopy(dummy.getHeaderId().getBytes(), 0, data, 4 + aLocal.length, 2);
109 System.arraycopy(dummy.getLocalFileDataLength().getBytes(), 0, data, 4 + aLocal.length + 2, 2);
110 System.arraycopy(dummyLocal, 0, data, 4 + aLocal.length + 4, dummyLocal.length);
111
112 }
113
114
115
116
117 @Test
118 public void testMerge() {
119 final byte[] local = ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] { a, dummy });
120 assertEquals(data.length, local.length, "local length");
121 for (int i = 0; i < local.length; i++) {
122 assertEquals(data[i], local[i], "local byte " + i);
123 }
124
125 final byte[] dummyCentral = dummy.getCentralDirectoryData();
126 final byte[] data2 = new byte[4 + aLocal.length + 4 + dummyCentral.length];
127 System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2);
128 System.arraycopy(dummy.getCentralDirectoryLength().getBytes(), 0, data2, 4 + aLocal.length + 2, 2);
129 System.arraycopy(dummyCentral, 0, data2, 4 + aLocal.length + 4, dummyCentral.length);
130
131 final byte[] central = ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] { a, dummy });
132 assertEquals(data2.length, central.length, "central length");
133 for (int i = 0; i < central.length; i++) {
134 assertEquals(data2[i], central[i], "central byte " + i);
135 }
136
137 }
138
139 @Test
140 public void testMergeWithUnparseableData() throws Exception {
141 final ZipExtraField d = new UnparseableExtraFieldData();
142 final byte[] b = UNRECOGNIZED_HEADER.getBytes();
143 d.parseFromLocalFileData(new byte[] { b[0], b[1], 1, 0 }, 0, 4);
144 final byte[] local = ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] { a, d });
145 assertEquals(data.length - 1, local.length, "local length");
146 for (int i = 0; i < local.length; i++) {
147 assertEquals(data[i], local[i], "local byte " + i);
148 }
149
150 final byte[] dCentral = d.getCentralDirectoryData();
151 final byte[] data2 = new byte[4 + aLocal.length + dCentral.length];
152 System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2);
153 System.arraycopy(dCentral, 0, data2, 4 + aLocal.length, dCentral.length);
154
155 final byte[] central = ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] { a, d });
156 assertEquals(data2.length, central.length, "central length");
157 for (int i = 0; i < central.length; i++) {
158 assertEquals(data2[i], central[i], "central byte " + i);
159 }
160
161 }
162
163
164
165
166 @Test
167 public void testParse() throws Exception {
168 final ZipExtraField[] ze = ExtraFieldUtils.parse(data);
169 assertEquals(2, ze.length, "number of fields");
170 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
171 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
172 assertTrue(ze[1] instanceof UnrecognizedExtraField, "type field 2");
173 assertEquals(1, ze[1].getLocalFileDataLength().getValue(), "data length field 2");
174
175 final byte[] data2 = new byte[data.length - 1];
176 System.arraycopy(data, 0, data2, 0, data2.length);
177 final Exception e = assertThrows(Exception.class, () -> ExtraFieldUtils.parse(data2), "data should be invalid");
178 assertEquals("Bad extra field starting at " + (4 + aLocal.length) + ". Block length of 1 bytes exceeds remaining data of 0 bytes.", e.getMessage(),
179 "message");
180 }
181
182 @Test
183 public void testParseCentral() throws Exception {
184 final ZipExtraField[] ze = ExtraFieldUtils.parse(data, false);
185 assertEquals(2, ze.length, "number of fields");
186 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
187 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
188 assertTrue(ze[1] instanceof UnrecognizedExtraField, "type field 2");
189 assertEquals(1, ze[1].getCentralDirectoryLength().getValue(), "data length field 2");
190
191 }
192
193 @Test
194 public void testParseTurnsArrayIndexOutOfBoundsIntoZipException() {
195 ExtraFieldUtils.register(AiobThrowingExtraField.class);
196 final AiobThrowingExtraField f = new AiobThrowingExtraField();
197 final byte[] d = new byte[4 + AiobThrowingExtraField.LENGTH];
198 System.arraycopy(f.getHeaderId().getBytes(), 0, d, 0, 2);
199 System.arraycopy(f.getLocalFileDataLength().getBytes(), 0, d, 2, 2);
200 System.arraycopy(f.getLocalFileDataData(), 0, d, 4, AiobThrowingExtraField.LENGTH);
201 final ZipException e = assertThrows(ZipException.class, () -> ExtraFieldUtils.parse(d), "data should be invalid");
202 assertEquals("Failed to parse corrupt ZIP extra field of type 1000", e.getMessage(), "message");
203 }
204
205 @Test
206 public void testParseWithRead() throws Exception {
207 ZipExtraField[] ze = ExtraFieldUtils.parse(data, true, ExtraFieldUtils.UnparseableExtraField.READ);
208 assertEquals(2, ze.length, "number of fields");
209 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
210 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
211 assertTrue(ze[1] instanceof UnrecognizedExtraField, "type field 2");
212 assertEquals(1, ze[1].getLocalFileDataLength().getValue(), "data length field 2");
213
214 final byte[] data2 = new byte[data.length - 1];
215 System.arraycopy(data, 0, data2, 0, data2.length);
216 ze = ExtraFieldUtils.parse(data2, true, ExtraFieldUtils.UnparseableExtraField.READ);
217 assertEquals(2, ze.length, "number of fields");
218 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
219 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
220 assertTrue(ze[1] instanceof UnparseableExtraFieldData, "type field 2");
221 assertEquals(4, ze[1].getLocalFileDataLength().getValue(), "data length field 2");
222 for (int i = 0; i < 4; i++) {
223 assertEquals(data2[data.length - 5 + i], ze[1].getLocalFileDataData()[i], "byte number " + i);
224 }
225 }
226
227 @Test
228 public void testParseWithSkip() throws Exception {
229 ZipExtraField[] ze = ExtraFieldUtils.parse(data, true, ExtraFieldUtils.UnparseableExtraField.SKIP);
230 assertEquals(2, ze.length, "number of fields");
231 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
232 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
233 assertTrue(ze[1] instanceof UnrecognizedExtraField, "type field 2");
234 assertEquals(1, ze[1].getLocalFileDataLength().getValue(), "data length field 2");
235
236 final byte[] data2 = new byte[data.length - 1];
237 System.arraycopy(data, 0, data2, 0, data2.length);
238 ze = ExtraFieldUtils.parse(data2, true, ExtraFieldUtils.UnparseableExtraField.SKIP);
239 assertEquals(1, ze.length, "number of fields");
240 assertTrue(ze[0] instanceof AsiExtraField, "type field 1");
241 assertEquals(040755, ((AsiExtraField) ze[0]).getMode(), "mode field 1");
242 }
243 }