Skip to content

Commit d0b580a

Browse files
committed
ValidatingObjectInputStream does not validate dynamic proxy interfaces
1 parent a478e88 commit d0b580a

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

src/changes/changes.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ The <action> type attribute can be add,update,fix,remove.
4545
<title>Apache Commons IO Release Notes</title>
4646
</properties>
4747
<body>
48-
<release version="2.21.0" date="YYYY-MM-DD" description="Version 2.20.1: Java 8 or later is required.">
48+
<release version="2.21.0" date="YYYY-MM-DD" description="Version 2.21.0: Java 8 or later is required.">
4949
<!-- FIX -->
5050
<action type="fix" dev="ggregory" due-to="Gary Gregory">When testing on Java 21 and up, enable -XX:+EnableDynamicAgentLoading.</action>
5151
<action type="fix" dev="ggregory" due-to="Gary Gregory">When testing on Java 24 and up, don't fail FileUtilsListFilesTest for a different behavior in the JRE.</action>
52+
<action type="fix" dev="ggregory" due-to="Stanislav Fort, Gary Gregory">ValidatingObjectInputStream does not validate dynamic proxy interfaces.</action>
5253
<!-- ADD -->
5354
<action dev="ggregory" type="add" due-to="strangelookingnerd, Gary Gregory">FileUtils#byteCountToDisplaySize() supports Zettabyte, Yottabyte, Ronnabyte and Quettabyte #763.</action>
5455
<action dev="ggregory" type="add" due-to="strangelookingnerd, Gary Gregory">Add org.apache.commons.io.FileUtils.ONE_RB #763.</action>

src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,29 @@ public ValidatingObjectInputStream reject(final String... patterns) {
462462
return this;
463463
}
464464

465+
/**
466+
* Checks that the given object's class name conforms to requirements and if so delegates to the superclass.
467+
* <p>
468+
* The reject list takes precedence over the accept list.
469+
* </p>
470+
*/
465471
@Override
466472
protected Class<?> resolveClass(final ObjectStreamClass osc) throws IOException, ClassNotFoundException {
467473
checkClassName(osc.getName());
468474
return super.resolveClass(osc);
469475
}
476+
477+
/**
478+
* Checks that the given names conform to requirements and if so delegates to the superclass.
479+
* <p>
480+
* The reject list takes precedence over the accept list.
481+
* </p>
482+
*/
483+
@Override
484+
protected Class<?> resolveProxyClass(final String[] interfaces) throws IOException, ClassNotFoundException {
485+
for (final String interfaceName : interfaces) {
486+
checkClassName(interfaceName);
487+
}
488+
return super.resolveProxyClass(interfaces);
489+
}
470490
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
20+
package org.apache.commons.io.serialization;
21+
22+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
24+
import static org.junit.jupiter.api.Assertions.assertTrue;
25+
26+
import java.io.IOException;
27+
import java.io.InvalidClassException;
28+
import java.io.Serializable;
29+
import java.lang.reflect.InvocationHandler;
30+
import java.lang.reflect.Method;
31+
import java.lang.reflect.Proxy;
32+
33+
import org.apache.commons.lang3.SerializationUtils;
34+
import org.junit.jupiter.api.Test;
35+
36+
/**
37+
* Tests {@link ValidatingObjectInputStream}.
38+
*/
39+
class ProxyTest {
40+
41+
public interface IFoo extends Serializable {
42+
43+
void foo();
44+
}
45+
46+
public static class InvocationHandlerImpl implements InvocationHandler, Serializable {
47+
48+
@Override
49+
public Object invoke(final Object proxy, final Method method, final Object[] args) {
50+
return "InvocationHandlerImpl.invoke()";
51+
}
52+
}
53+
54+
Object newProxy() {
55+
return Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class<?>[] { IFoo.class }, new InvocationHandlerImpl());
56+
}
57+
58+
@Test
59+
void testAcceptProxy() throws IOException, ClassNotFoundException {
60+
final Object proxy = newProxy();
61+
final byte[] serialized = SerializationUtils.serialize((Serializable) proxy);
62+
final Class<IFoo> ifaceClass = IFoo.class;
63+
// @formatter:off
64+
try (ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
65+
.setByteArray(serialized)
66+
.accept("*")
67+
.get()) {
68+
// @formatter:on
69+
assertTrue(assertInstanceOf(ifaceClass, vois.readObject()).toString().endsWith("InvocationHandlerImpl.invoke()"));
70+
}
71+
}
72+
73+
@Test
74+
void testRejectProxy() throws IOException, ClassNotFoundException {
75+
final Object proxy = newProxy();
76+
final byte[] serialized = SerializationUtils.serialize((Serializable) proxy);
77+
final Class<IFoo> ifaceClass = IFoo.class;
78+
// @formatter:off
79+
try (ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
80+
.setByteArray(serialized)
81+
.accept("*")
82+
.reject(ifaceClass)
83+
.get()) {
84+
// @formatter:on
85+
assertThrows(InvalidClassException.class, vois::readObject);
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)