1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.io.serialization;
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InvalidClassException;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.util.HashMap;
33 import java.util.UUID;
34 import java.util.regex.Pattern;
35
36 import org.apache.commons.io.input.BoundedInputStream;
37 import org.apache.commons.io.serialization.ValidatingObjectInputStream.Builder;
38 import org.apache.commons.lang3.SerializationUtils;
39 import org.junit.jupiter.api.BeforeEach;
40 import org.junit.jupiter.api.Test;
41
42
43
44
45 class ValidatingObjectInputStreamTest extends AbstractCloseableListTest {
46
47 private static final ClassNameMatcher ALWAYS_TRUE = className -> true;
48 private MockSerializedClass testObject;
49
50 private InputStream testStream;
51
52 private void assertSerialization(final ObjectInputStream ois) throws ClassNotFoundException, IOException {
53 final MockSerializedClass result = (MockSerializedClass) ois.readObject();
54 assertEquals(testObject, result);
55 }
56
57 private Builder newBuilder() {
58 return ValidatingObjectInputStream.builder().setInputStream(testStream);
59 }
60
61 private ValidatingObjectInputStream newFixture() throws IOException {
62 return newBuilder().get();
63 }
64
65 @BeforeEach
66 public void setupMockSerializedClass() throws IOException {
67 testObject = new MockSerializedClass(UUID.randomUUID().toString());
68 final ByteArrayOutputStream bos = addCloseable(new ByteArrayOutputStream());
69 final ObjectOutputStream oos = addCloseable(new ObjectOutputStream(bos));
70 oos.writeObject(testObject);
71 testStream = addCloseable(new ByteArrayInputStream(bos.toByteArray()));
72 }
73
74 @Test
75 void testAcceptCustomMatcherBuilder() throws Exception {
76 assertSerialization(addCloseable(newBuilder().accept(ALWAYS_TRUE).get()));
77 }
78
79 @Test
80 void testAcceptCustomMatcherInstance() throws Exception {
81 assertSerialization(addCloseable(newFixture()).accept(ALWAYS_TRUE));
82 }
83
84 @Test
85 void testAcceptOneFail() throws Exception {
86 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture()).accept(Integer.class)));
87 }
88
89 @Test
90 void testAcceptOnePassBuilder() throws Exception {
91 assertSerialization(addCloseable(newBuilder().accept(MockSerializedClass.class).get()));
92 }
93
94 @Test
95 void testAcceptOnePassInstance() throws Exception {
96 assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class));
97 }
98
99 @Test
100 void testAcceptPatternBuilder() throws Exception {
101 assertSerialization(addCloseable(newBuilder().accept(Pattern.compile(".*MockSerializedClass.*")).get()));
102 }
103
104 @Test
105 void testAcceptPatternInstance() throws Exception {
106 assertSerialization(addCloseable(newFixture()).accept(Pattern.compile(".*MockSerializedClass.*")));
107 }
108
109 @Test
110 void testAcceptWildcardBuilder() throws Exception {
111 assertSerialization(addCloseable(newBuilder().accept("org.apache.commons.io.*").get()));
112 }
113
114 @Test
115 void testAcceptWildcardInstance() throws Exception {
116 assertSerialization(addCloseable(newFixture()).accept("org.apache.commons.io.*"));
117 }
118
119 @Test
120 void testBuildDefault() throws Exception {
121 final byte[] serialized = SerializationUtils.serialize("");
122 try (InputStream is = newBuilder().setInputStream(new ByteArrayInputStream(serialized)).get()) {
123
124 }
125 }
126
127 @Test
128 void testConstructor() throws Exception {
129 assertSerialization(addCloseable(newFixture()).accept(ALWAYS_TRUE));
130 }
131
132 @Test
133 void testCustomInvalidMethod() {
134 final class CustomVOIS extends ValidatingObjectInputStream {
135 CustomVOIS(final InputStream is) throws IOException {
136 super(is);
137 }
138
139 @Override
140 protected void invalidClassNameFound(final String className) throws InvalidClassException {
141 throw new RuntimeException("Custom exception");
142 }
143 }
144
145 assertThrows(RuntimeException.class, () -> assertSerialization(addCloseable(new CustomVOIS(testStream)).reject(Integer.class)));
146 }
147
148 @Test
149 void testExceptionIncludesClassName() throws Exception {
150 final InvalidClassException ice = assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture())));
151 final String name = MockSerializedClass.class.getName();
152 assertTrue(ice.getMessage().contains(name), "Expecting message to contain " + name);
153 }
154
155
156
157
158 @SuppressWarnings({ "unchecked" })
159 @Test
160 void testJavadocExample() throws Exception {
161
162
163 final HashMap<String, Integer> map1 = new HashMap<>();
164 map1.put("1", 1);
165
166 final byte[] byteArray;
167 try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
168 ObjectOutputStream oos = new ObjectOutputStream(baos)) {
169 oos.writeObject(map1);
170 oos.flush();
171 byteArray = baos.toByteArray();
172 }
173
174 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
175 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
176 .accept(HashMap.class, Number.class, Integer.class)
177 .setInputStream(bais)
178 .get()) {
179
180 final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
181 assertEquals(map1, map2);
182 }
183
184 final ObjectStreamClassPredicate predicate = new ObjectStreamClassPredicate()
185 .accept(HashMap.class, Number.class, Integer.class);
186 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
187 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
188 .setPredicate(predicate)
189 .setInputStream(bais)
190 .get()) {
191
192 final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
193 assertEquals(map1, map2);
194 }
195
196 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
197 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
198 .accept(HashMap.class, Number.class, Integer.class)
199 .setInputStream(BoundedInputStream.builder()
200 .setMaxCount(10_000)
201 .setOnMaxCount((remains, count) -> {
202 throw new IllegalArgumentException("Input exceeds limit.");
203 })
204 .setInputStream(bais)
205 .get())
206 .get()) {
207
208 final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
209 assertEquals(map1, map2);
210 }
211
212 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
213 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
214 .accept(HashMap.class, Number.class, Integer.class)
215 .setInputStream(BoundedInputStream.builder()
216 .setMaxCount(10)
217 .setOnMaxCount((remains, count) -> {
218 throw new IllegalArgumentException("Input exceeds limit.");
219 })
220 .setInputStream(bais)
221 .get())
222 .get()) {
223
224 final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> vois.readObject());
225 assertEquals("Input exceeds limit.", e.getMessage());
226 }
227
228 }
229
230 @Test
231 void testNoAccept() {
232 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture())));
233 }
234
235 @Test
236 void testOurTestClassAcceptedFirst() throws Exception {
237 assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class, Integer.class));
238 }
239
240 @Test
241 void testOurTestClassAcceptedFirstWildcard() throws Exception {
242 assertSerialization(addCloseable(newFixture()).accept("*MockSerializedClass", "*Integer"));
243 }
244
245 @Test
246 void testOurTestClassAcceptedSecond() throws Exception {
247 assertSerialization(addCloseable(newFixture()).accept(Integer.class, MockSerializedClass.class));
248 }
249
250 @Test
251 void testOurTestClassAcceptedSecondWildcard() throws Exception {
252 assertSerialization(addCloseable(newFixture()).accept("*Integer", "*MockSerializedClass"));
253 }
254
255 @Test
256 void testOurTestClassNotAccepted() {
257 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture()).accept(Integer.class)));
258 }
259
260 @Test
261 void testOurTestClassOnlyAccepted() throws Exception {
262 assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class));
263 }
264
265 @Test
266 void testRejectBuilder() {
267 assertThrows(InvalidClassException.class,
268 () -> assertSerialization(addCloseable(newBuilder().accept(Long.class).reject(MockSerializedClass.class, Integer.class).get())));
269 }
270
271 @Test
272 void testRejectCustomMatcherBuilder() {
273 assertThrows(InvalidClassException.class,
274 () -> assertSerialization(addCloseable(newBuilder().accept(MockSerializedClass.class).reject(ALWAYS_TRUE).get())));
275 }
276
277 @Test
278 void testRejectCustomMatcherInstance() {
279 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class).reject(ALWAYS_TRUE)));
280 }
281
282 @Test
283 void testRejectInstance() {
284 assertThrows(InvalidClassException.class,
285 () -> assertSerialization(addCloseable(newFixture()).accept(Long.class).reject(MockSerializedClass.class, Integer.class)));
286 }
287
288 @Test
289 void testRejectOnly() {
290 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture()).reject(Integer.class)));
291 }
292
293 @Test
294 void testRejectPattern() {
295 assertThrows(InvalidClassException.class,
296 () -> assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class).reject(Pattern.compile("org\\..*"))));
297 }
298
299 @Test
300 void testRejectPatternBuilder() {
301 assertThrows(InvalidClassException.class,
302 () -> assertSerialization(addCloseable(newBuilder().accept(MockSerializedClass.class).reject(Pattern.compile("org\\..*")).get())));
303 }
304
305 @Test
306 void testRejectPrecedenceBuilder() {
307 assertThrows(InvalidClassException.class,
308 () -> assertSerialization(addCloseable(newBuilder().accept(MockSerializedClass.class).reject(MockSerializedClass.class, Integer.class).get())));
309 }
310
311 @Test
312 void testRejectPrecedenceInstance() {
313 assertThrows(InvalidClassException.class,
314 () -> assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class).reject(MockSerializedClass.class, Integer.class)));
315 }
316
317 @Test
318 void testRejectWildcardBuilder() {
319 assertThrows(InvalidClassException.class,
320 () -> assertSerialization(addCloseable(newBuilder().accept(MockSerializedClass.class).reject("org.*").get())));
321 }
322
323 @Test
324 void testRejectWildcardInstance() {
325 assertThrows(InvalidClassException.class, () -> assertSerialization(addCloseable(newFixture()).accept(MockSerializedClass.class).reject("org.*")));
326 }
327
328 @Test
329 void testReuseConfiguration() throws Exception {
330
331 final HashMap<String, Integer> map1 = new HashMap<>();
332 map1.put("1", 1);
333
334 final byte[] byteArray;
335 try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
336 ObjectOutputStream oos = new ObjectOutputStream(baos)) {
337 oos.writeObject(map1);
338 oos.flush();
339 byteArray = baos.toByteArray();
340 }
341
342 final ObjectStreamClassPredicate predicate = new ObjectStreamClassPredicate().accept(HashMap.class, Number.class, Integer.class);
343 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
344 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder().setPredicate(predicate).setInputStream(bais).get()) {
345
346 assertEquals(map1, vois.readObjectCast());
347 }
348 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
349 ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder().setPredicate(predicate).setInputStream(bais).get()) {
350
351 assertEquals(map1, vois.readObjectCast());
352 }
353
354 final Builder builder = ValidatingObjectInputStream.builder().setPredicate(predicate);
355 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
356 ValidatingObjectInputStream vois = builder.setInputStream(bais).get()) {
357
358 assertEquals(map1, vois.readObjectCast());
359 }
360 try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
361 ValidatingObjectInputStream vois = builder.setInputStream(bais).get()) {
362
363 assertEquals(map1, vois.readObjectCast());
364 }
365 }
366 }