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 <garydgreg...@gmail.com>
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);
+        }
+    }
+}

Reply via email to