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