This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git
The following commit(s) were added to refs/heads/master by this push:
new 8f273f2d Reject deserialization of bytes containing a proxy class
descriptor
8f273f2d is described below
commit 8f273f2d0910089d32c89263165b124bb37a0e6f
Author: Alex Herbert <[email protected]>
AuthorDate: Fri Aug 21 19:24:57 2026 +0100
Reject deserialization of bytes containing a proxy class descriptor
---
.../commons/rng/core/source32/JDKRandom.java | 17 ++++++++
.../commons/rng/core/source32/JDKRandomTest.java | 50 ++++++++++++++++++++++
src/changes/changes.xml | 4 ++
3 files changed, 71 insertions(+)
diff --git
a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java
b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java
index 3c36c868..8a457139 100644
---
a/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java
+++
b/commons-rng-core/src/main/java/org/apache/commons/rng/core/source32/JDKRandom.java
@@ -78,6 +78,23 @@ public class JDKRandom extends IntProvider {
}
return super.resolveClass(osc);
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The only expected class is {@link java.util.Random} which is not
a proxy
+ * class. The allowlist enforced by {@link
#resolveClass(ObjectStreamClass)}
+ * must also hold on the proxy class-resolution path so this method
+ * unconditionally rejects the stream.
+ *
+ * @throws IllegalStateException Always: no legitimate stream contains
a proxy
+ * class descriptor.
+ */
+ @Override
+ protected Class<?> resolveProxyClass(final String[] interfaces) throws
IOException,
+ ClassNotFoundException {
+ throw new IllegalStateException("Stream contains a proxy class
descriptor");
+ }
}
/**
diff --git
a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java
b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java
index fde0913a..72f28dd7 100644
---
a/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java
+++
b/commons-rng-core/src/test/java/org/apache/commons/rng/core/source32/JDKRandomTest.java
@@ -21,6 +21,9 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.Random;
import org.apache.commons.rng.RandomProviderState;
@@ -58,6 +61,19 @@ class JDKRandomTest {
}
}
+ /**
+ * An InvocationHandler that is Serializable so a dynamic proxy instance
+ * can be written to an ObjectOutputStream.
+ */
+ static class SerializableInvocationHandler implements InvocationHandler,
Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ return null;
+ }
+ }
+
@Test
void testReferenceCode() {
final long refSeed = -1357111213L;
@@ -125,4 +141,38 @@ class JDKRandomTest {
final JDKRandom rng = new JDKRandom(13L);
Assertions.assertThrows(IllegalStateException.class, () ->
rng.restoreState(dummyState));
}
+
+ /**
+ * Test the deserialization code rejects a state containing a proxy class
descriptor.
+ * The java.util.Random allowlist must hold on the proxy class-resolution
path;
+ * no legitimate state stream contains a proxy class.
+ *
+ * @throws IOException Signals that an I/O exception has occurred.
+ */
+ @Test
+ void testRestoreWithProxyClass() throws IOException {
+ // Serialize a dynamic proxy instance
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+
oos.writeObject(Proxy.newProxyInstance(JDKRandomTest.class.getClassLoader(),
+ new Class<?>[] {Runnable.class}, new
SerializableInvocationHandler()));
+ }
+
+ // Compose the size with the state.
+ // This is what is expected by the JDKRandom class.
+ final byte[] state = bos.toByteArray();
+ final int stateSize = state.length;
+ final byte[] sizeAndState = new byte[4 + stateSize];
+ System.arraycopy(NumberFactory.makeByteArray(stateSize), 0,
sizeAndState, 0, 4);
+ System.arraycopy(state, 0, sizeAndState, 4, stateSize);
+
+ final RandomProviderDefaultState dummyState = new
RandomProviderDefaultState(sizeAndState);
+
+ final JDKRandom rng = new JDKRandom(13L);
+ final IllegalStateException ex =
Assertions.assertThrows(IllegalStateException.class,
+ () -> rng.restoreState(dummyState));
+ // The stream must be rejected on the proxy class-resolution path, not
by a
+ // downstream failure to resolve a normal class.
+ Assertions.assertTrue(ex.getMessage().contains("proxy"), () ->
ex.getMessage());
+ }
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ad886f69..dc18e076 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -56,6 +56,10 @@ If the output is not quite correct, check for invisible
trailing spaces!
<release version="1.8" date="TBD" description="
New features, updates and bug fixes (requires Java 8).
">
+ <action dev="aherbert" type="update" due-to="Security scan, Alex
Herbert">
+ "JDKRandom": Fail fast when restoring from a saved state if the
+ serialized bytes contains a proxy class descriptor.
+ </action>
<action dev="aherbert" type="fix" due-to="Security scan, Alex Herbert"
issue="RNG-194">
Samplers to validate parameters are finite to avoid creating a sampler
that
returns NaN, infinite or degenerate-constant samples forever; or
enters an