This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push:
new d0b580a8c ValidatingObjectInputStream does not validate dynamic proxy
interfaces
d0b580a8c is described below
commit d0b580a8c620980674ddd2fe81b472dbed26748f
Author: Gary D. Gregory <[email protected]>
AuthorDate: Wed Aug 20 07:52:43 2025 -0400
ValidatingObjectInputStream does not validate dynamic proxy interfaces
---
src/changes/changes.xml | 3 +-
.../serialization/ValidatingObjectInputStream.java | 20 +++++
.../apache/commons/io/serialization/ProxyTest.java | 88 ++++++++++++++++++++++
3 files changed, 110 insertions(+), 1 deletion(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d3bbf77d6..136dd3e9a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -45,10 +45,11 @@ The <action> type attribute can be add,update,fix,remove.
<title>Apache Commons IO Release Notes</title>
</properties>
<body>
- <release version="2.21.0" date="YYYY-MM-DD" description="Version 2.20.1:
Java 8 or later is required.">
+ <release version="2.21.0" date="YYYY-MM-DD" description="Version 2.21.0:
Java 8 or later is required.">
<!-- FIX -->
<action type="fix" dev="ggregory" due-to="Gary
Gregory">When testing on Java 21 and up, enable
-XX:+EnableDynamicAgentLoading.</action>
<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>
+ <action type="fix" dev="ggregory" due-to="Stanislav Fort,
Gary Gregory">ValidatingObjectInputStream does not validate dynamic proxy
interfaces.</action>
<!-- ADD -->
<action dev="ggregory" type="add"
due-to="strangelookingnerd, Gary Gregory">FileUtils#byteCountToDisplaySize()
supports Zettabyte, Yottabyte, Ronnabyte and Quettabyte #763.</action>
<action dev="ggregory" type="add"
due-to="strangelookingnerd, Gary Gregory">Add
org.apache.commons.io.FileUtils.ONE_RB #763.</action>
diff --git
a/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
b/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
index c3c06a32b..6141cc59b 100644
---
a/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
+++
b/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
@@ -462,9 +462,29 @@ public ValidatingObjectInputStream reject(final String...
patterns) {
return this;
}
+ /**
+ * Checks that the given object's class name conforms to requirements and
if so delegates to the superclass.
+ * <p>
+ * The reject list takes precedence over the accept list.
+ * </p>
+ */
@Override
protected Class<?> resolveClass(final ObjectStreamClass osc) throws
IOException, ClassNotFoundException {
checkClassName(osc.getName());
return super.resolveClass(osc);
}
+
+ /**
+ * Checks that the given names conform to requirements and if so delegates
to the superclass.
+ * <p>
+ * The reject list takes precedence over the accept list.
+ * </p>
+ */
+ @Override
+ protected Class<?> resolveProxyClass(final String[] interfaces) throws
IOException, ClassNotFoundException {
+ for (final String interfaceName : interfaces) {
+ checkClassName(interfaceName);
+ }
+ return super.resolveProxyClass(interfaces);
+ }
}
diff --git a/src/test/java/org/apache/commons/io/serialization/ProxyTest.java
b/src/test/java/org/apache/commons/io/serialization/ProxyTest.java
new file mode 100644
index 000000000..64b82551e
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/serialization/ProxyTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.io.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link ValidatingObjectInputStream}.
+ */
+class ProxyTest {
+
+ public interface IFoo extends Serializable {
+
+ void foo();
+ }
+
+ public static class InvocationHandlerImpl implements InvocationHandler,
Serializable {
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final
Object[] args) {
+ return "InvocationHandlerImpl.invoke()";
+ }
+ }
+
+ Object newProxy() {
+ return Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new
Class<?>[] { IFoo.class }, new InvocationHandlerImpl());
+ }
+
+ @Test
+ void testAcceptProxy() throws IOException, ClassNotFoundException {
+ final Object proxy = newProxy();
+ final byte[] serialized = SerializationUtils.serialize((Serializable)
proxy);
+ final Class<IFoo> ifaceClass = IFoo.class;
+ // @formatter:off
+ try (ValidatingObjectInputStream vois =
ValidatingObjectInputStream.builder()
+ .setByteArray(serialized)
+ .accept("*")
+ .get()) {
+ // @formatter:on
+ assertTrue(assertInstanceOf(ifaceClass,
vois.readObject()).toString().endsWith("InvocationHandlerImpl.invoke()"));
+ }
+ }
+
+ @Test
+ void testRejectProxy() throws IOException, ClassNotFoundException {
+ final Object proxy = newProxy();
+ final byte[] serialized = SerializationUtils.serialize((Serializable)
proxy);
+ final Class<IFoo> ifaceClass = IFoo.class;
+ // @formatter:off
+ try (ValidatingObjectInputStream vois =
ValidatingObjectInputStream.builder()
+ .setByteArray(serialized)
+ .accept("*")
+ .reject(ifaceClass)
+ .get()) {
+ // @formatter:on
+ assertThrows(InvalidClassException.class, vois::readObject);
+ }
+ }
+}