This is an automated email from the ASF dual-hosted git repository. elecharny pushed a commit to branch 2.0.X in repository https://gitbox.apache.org/repos/asf/mina.git
commit 8b1dadb55b1d3f6b17f4ded10179e6b253ff1cc9 Author: Emmanuel Lécharny <[email protected]> AuthorDate: Thu Apr 23 10:51:03 2026 +0200 Added patch regarding whitelist bypassing backported from mina-2.2.X --- .../apache/mina/core/buffer/AbstractIoBuffer.java | 47 ++++++------ .../org/apache/mina/core/buffer/IoBufferTest.java | 88 +++++++++++++++++++--- 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java index 1a2463a6a..d5d54f9e5 100644 --- a/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java +++ b/mina-core/src/main/java/org/apache/mina/core/buffer/AbstractIoBuffer.java @@ -88,6 +88,7 @@ public abstract class AbstractIoBuffer extends IoBuffer { /** A mask for an int */ private static final long INT_MASK = 0xFFFFFFFFL; + /** A list of acceptable classes for deserialization */ private final List<ClassNameMatcher> acceptMatchers = new ArrayList<>(); /** @@ -2182,6 +2183,14 @@ public abstract class AbstractIoBuffer extends IoBuffer { case 1: // Serializable class String className = readUTF(); + + // Only accept classes that are listed as acceptable + // Apply class filter BEFORE calling Class.forName + if (!acceptMatchers.stream().anyMatch(m -> m.matches(className))) { + throw new ClassNotFoundException("Class not in accept list " + className); + } + + // Use initialize=false to prevent static block execution during class loading Class<?> clazz = Class.forName(className, true, classLoader); return ObjectStreamClass.lookup(clazz); @@ -2193,32 +2202,24 @@ public abstract class AbstractIoBuffer extends IoBuffer { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - Class<?> clazz = desc.forClass(); + String className = desc.getName(); - if (clazz == null) { - String name = desc.getName(); + // apply acceptMatchers filter before any Class.forName() call, + // regardless of whether forClass() is null or not + if (!acceptMatchers.stream().anyMatch(m -> m.matches(className))) { + throw new ClassNotFoundException("Class not in accept list " + className); + } + + Class<?> clazz = desc.forClass(); - try { - return Class.forName(name, false, classLoader); - } catch (ClassNotFoundException ex) { - return super.resolveClass(desc); - } - } else { - boolean found = false; - String className = desc.getName(); - - for (ClassNameMatcher matcher : acceptMatchers) { - if (matcher.matches(className)) { - found = true; - break; - } - } + if (clazz != null) { + return clazz; + } - if (found) { - return clazz; - } - - throw new ClassNotFoundException(); + try { + return Class.forName(className, false, classLoader); + } catch (ClassNotFoundException ex) { + return super.resolveClass(desc); } } }) { diff --git a/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferTest.java b/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferTest.java index e969428a4..04c792afd 100644 --- a/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferTest.java +++ b/mina-core/src/test/java/org/apache/mina/core/buffer/IoBufferTest.java @@ -44,6 +44,7 @@ import java.util.List; import org.apache.mina.core.buffer.matcher.RegexpClassNameMatcher; import org.apache.mina.core.buffer.matcher.WildcardClassNameMatcher; import org.apache.mina.util.Bar; +import org.apache.mina.util.Foo; import org.junit.Test; /** @@ -53,10 +54,10 @@ import org.junit.Test; */ public class IoBufferTest { - private static interface NonserializableInterface { + private static interface NonSerializableInterface { } - public static class NonserializableClass { + public static class NonSerializableClass { } /** @@ -388,8 +389,29 @@ public class IoBufferTest { assertNotSame(o, o2); } + @Test(expected=ClassNotFoundException.class) + public void testObjectSerializationReject() throws Exception { + IoBuffer buf = IoBuffer.allocate(16); + buf.setAutoExpand(true); + List<Object> o = new ArrayList<>(); + o.add(new Date()); + o.add(long.class); + + // We don't accept type 0 class (long) + buf.accept(ArrayList.class.getName(), Date.class.getName()); + + // Test writing an object. + buf.putObject(o); + + // Test reading an object. + buf.clear(); + + // The call should fail as long is not accepted + buf.getObject(); + } + @Test - public void testNonserializableClass() throws Exception { + public void testSerializableClass() throws Exception { Class<?> c = String.class; IoBuffer buffer = IoBuffer.allocate(16); @@ -407,7 +429,7 @@ public class IoBufferTest { } @Test - public void testNonserializableClassAcceptWildcard() throws Exception { + public void testSerializableClassAcceptWildcard() throws Exception { Class<?> c = String.class; IoBuffer buffer = IoBuffer.allocate(16); @@ -444,8 +466,8 @@ public class IoBufferTest { assertSame(c, o); } - @Test(expected=ClassNotFoundException.class) - public void testNonserializableClassReject() throws Exception { + @Test(expected=BufferDataException.class) + public void testSerializableClassReject() throws Exception { Class<?> c = String.class; IoBuffer buffer = IoBuffer.allocate(16); @@ -460,13 +482,43 @@ public class IoBufferTest { } @Test - public void testNonserializableInterface() throws Exception { - Class<?> c = NonserializableInterface.class; + public void testNonserializableInterfaceAccept() throws Exception { + Class<?> c = NonSerializableInterface.class; + + IoBuffer buffer = IoBuffer.allocate(16); + buffer.setAutoExpand(true); + buffer.putObject(c); + buffer.accept(NonSerializableInterface.class.getName()); + + buffer.flip(); + Object o = buffer.getObject(); + + assertEquals(c, o); + assertSame(c, o); + } + + @Test(expected=ClassNotFoundException.class) + public void testNonserializableInterfaceReject() throws Exception { + Class<?> c = NonSerializableInterface.class; + + IoBuffer buffer = IoBuffer.allocate(16); + buffer.setAutoExpand(true); + buffer.putObject(c); + + buffer.flip(); + + // We must get an error + buffer.getObject(); + } + + @Test + public void testNonserializableClassAccept() throws Exception { + Class<?> c = NonSerializableClass.class; IoBuffer buffer = IoBuffer.allocate(16); buffer.setAutoExpand(true); buffer.putObject(c); - buffer.accept(NonserializableInterface.class.getName()); + buffer.accept(NonSerializableClass.class.getName()); buffer.flip(); Object o = buffer.getObject(); @@ -475,6 +527,21 @@ public class IoBufferTest { assertSame(c, o); } + + @Test(expected=ClassNotFoundException.class) + public void testNonserializableClassReject() throws Exception { + Class<?> c = NonSerializableClass.class; + + IoBuffer buffer = IoBuffer.allocate(16); + buffer.setAutoExpand(true); + buffer.putObject(c); + + buffer.flip(); + + // The call must fail + buffer.getObject(); + } + @Test public void testAllocate() throws Exception { for (int i = 10; i < 1048576 * 2; i = i * 11 / 10) // increase by 10% @@ -1007,7 +1074,10 @@ public class IoBufferTest { // Test writing an object. buf.putObject(expected); + + // We must accept all the classes, including the parents. buf.accept(Bar.class.getName()); + buf.accept(Foo.class.getName()); // Test reading an object. buf.clear();
