This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git
The following commit(s) were added to refs/heads/master by this push:
new ddf3bebbe Re-validate entries in PredicatedMap/PredicatedCollection
readObject (#682)
ddf3bebbe is described below
commit ddf3bebbe6a1b9b19e1b7352bb8f075e4f700cec
Author: Dexter.k <[email protected]>
AuthorDate: Wed Jun 17 11:24:45 2026 +0000
Re-validate entries in PredicatedMap/PredicatedCollection readObject (#682)
* re-validate entries on deserialization in predicated decorators
* preserve cause and check null invariants in predicated readObject
* throw InvalidObjectException for null fields in readObject
---
.../collection/PredicatedCollection.java | 25 ++++++++++++++++++++++
.../commons/collections4/map/PredicatedMap.java | 9 ++++++++
.../collection/PredicatedCollectionTest.java | 24 +++++++++++++++++++++
.../collections4/map/PredicatedMapTest.java | 25 ++++++++++++++++++++++
4 files changed, 83 insertions(+)
diff --git
a/src/main/java/org/apache/commons/collections4/collection/PredicatedCollection.java
b/src/main/java/org/apache/commons/collections4/collection/PredicatedCollection.java
index 24743f0c6..8727fef66 100644
---
a/src/main/java/org/apache/commons/collections4/collection/PredicatedCollection.java
+++
b/src/main/java/org/apache/commons/collections4/collection/PredicatedCollection.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.collections4.collection;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -409,6 +412,28 @@ public class PredicatedCollection<E> extends
AbstractCollectionDecorator<E> {
return decorated().addAll(coll);
}
+ /**
+ * Deserializes the collection in using a custom routine.
+ *
+ * @param in the input stream
+ * @throws IOException if an error occurs while reading from the stream
+ * @throws ClassNotFoundException if an object read from the stream cannot
be loaded
+ */
+ private void readObject(final ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ in.defaultReadObject();
+ if (decorated() == null) {
+ throw new InvalidObjectException("Null collection");
+ }
+ if (predicate == null) {
+ throw new InvalidObjectException("Null predicate");
+ }
+ try {
+ decorated().forEach(this::validate);
+ } catch (final IllegalArgumentException ex) {
+ throw (InvalidObjectException) new
InvalidObjectException(ex.getMessage()).initCause(ex);
+ }
+ }
+
/**
* Validates the object being added to ensure it matches the predicate.
* <p>
diff --git
a/src/main/java/org/apache/commons/collections4/map/PredicatedMap.java
b/src/main/java/org/apache/commons/collections4/map/PredicatedMap.java
index 66b904ffe..d2c0b4d46 100644
--- a/src/main/java/org/apache/commons/collections4/map/PredicatedMap.java
+++ b/src/main/java/org/apache/commons/collections4/map/PredicatedMap.java
@@ -17,6 +17,7 @@
package org.apache.commons.collections4.map;
import java.io.IOException;
+import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@@ -155,6 +156,14 @@ public class PredicatedMap<K, V>
private void readObject(final ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
map = (Map<K, V>) in.readObject(); // (1)
+ if (map == null) {
+ throw new InvalidObjectException("Null map");
+ }
+ try {
+ map.forEach(this::validate);
+ } catch (final IllegalArgumentException ex) {
+ throw (InvalidObjectException) new
InvalidObjectException(ex.getMessage()).initCause(ex);
+ }
}
/**
diff --git
a/src/test/java/org/apache/commons/collections4/collection/PredicatedCollectionTest.java
b/src/test/java/org/apache/commons/collections4/collection/PredicatedCollectionTest.java
index 0ab9d4fee..c79847236 100644
---
a/src/test/java/org/apache/commons/collections4/collection/PredicatedCollectionTest.java
+++
b/src/test/java/org/apache/commons/collections4/collection/PredicatedCollectionTest.java
@@ -19,12 +19,18 @@ package org.apache.commons.collections4.collection;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.functors.NotNullPredicate;
import org.apache.commons.collections4.functors.TruePredicate;
import org.junit.jupiter.api.Test;
@@ -76,6 +82,24 @@ public class PredicatedCollectionTest<E> extends
AbstractCollectionTest<E> {
return decorateCollection(new ArrayList<>(), truePredicate);
}
+ @Test
+ void testDeserializeRejectsElementFailingPredicate() throws Exception {
+ final PredicatedCollection<E> coll = (PredicatedCollection<E>)
decorateCollection(
+ new ArrayList<>(), NotNullPredicate.<E>notNullPredicate());
+ // a crafted stream can carry an element that never passed add();
mimic it by
+ // writing one straight into the decorated collection
+ coll.decorated().add(null);
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
+ oos.writeObject(coll);
+ }
+ assertThrows(InvalidObjectException.class, () -> {
+ try (ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(out.toByteArray()))) {
+ ois.readObject();
+ }
+ });
+ }
+
public Collection<E> makeTestCollection() {
return decorateCollection(new ArrayList<>(), testPredicate);
}
diff --git
a/src/test/java/org/apache/commons/collections4/map/PredicatedMapTest.java
b/src/test/java/org/apache/commons/collections4/map/PredicatedMapTest.java
index 5173e02f0..462b223b4 100644
--- a/src/test/java/org/apache/commons/collections4/map/PredicatedMapTest.java
+++ b/src/test/java/org/apache/commons/collections4/map/PredicatedMapTest.java
@@ -21,12 +21,18 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.collections4.IterableMap;
import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.functors.NotNullPredicate;
import org.apache.commons.collections4.functors.TruePredicate;
import org.junit.jupiter.api.Test;
@@ -62,6 +68,25 @@ public class PredicatedMapTest<K, V> extends
AbstractIterableMapTest<K, V> {
return decorateMap(new HashMap<>(), testPredicate, testPredicate);
}
+ @Test
+ @SuppressWarnings("unchecked")
+ void testDeserializeRejectsEntryFailingPredicate() throws Exception {
+ final PredicatedMap<K, V> map = (PredicatedMap<K, V>) decorateMap(new
HashMap<>(),
+ NotNullPredicate.notNullPredicate(), null);
+ // a crafted stream can carry an entry that never passed put(); mimic
it by
+ // writing one straight into the decorated map
+ map.decorated().put(null, (V) "value");
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
+ oos.writeObject(map);
+ }
+ assertThrows(InvalidObjectException.class, () -> {
+ try (ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(out.toByteArray()))) {
+ ois.readObject();
+ }
+ });
+ }
+
@Test
@SuppressWarnings("unchecked")
void testEntrySet() {