Description: Disable the deserialization of the functors classes unless
 the system property org.apache.commons.collections.enableUnsafeSerialization
 is set to true.
 .
 This fixes a vulnerability in unsafe applications deserializing objects
 from untrusted sources without sanitizing the input data.
 .
 https://blogs.apache.org/foundation/entry/apache_commons_statement_to_widespread
Origin: backport, http://svn.apache.org/r1713845
Bug: https://issues.apache.org/jira/browse/COLLECTIONS-580
--- a/src/java/org/apache/commons/collections/functors/CloneTransformer.java
+++ b/src/java/org/apache/commons/collections/functors/CloneTransformer.java
@@ -68,4 +68,8 @@
         return PrototypeFactory.getInstance(input).create();
     }
 
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(CloneTransformer.class);
+        is.defaultReadObject();
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/ForClosure.java
+++ b/src/java/org/apache/commons/collections/functors/ForClosure.java
@@ -102,4 +102,8 @@
         return iCount;
     }
 
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(ForClosure.class);
+        is.defaultReadObject();
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/FunctorUtils.java
+++ b/src/java/org/apache/commons/collections/functors/FunctorUtils.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.collections.functors;
 
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -34,6 +36,10 @@
  */
 class FunctorUtils {
     
+    /** System property key to enable unsafe serialization */
+    final static String UNSAFE_SERIALIZABLE_PROPERTY
+        = "org.apache.commons.collections.enableUnsafeSerialization";
+
     /**
      * Restricted constructor.
      */
@@ -152,4 +158,32 @@
         }
     }
 
+    /**
+     * Package-private helper method to check if serialization support is
+     * enabled for unsafe classes.
+     *
+     * @param clazz  the clazz to check for serialization support
+     * @throws UnsupportedOperationException if unsafe serialization is disabled
+     */
+    static void checkUnsafeSerialization(Class clazz) {
+        String unsafeSerializableProperty;
+        
+        try {
+            unsafeSerializableProperty = 
+                (String) AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run() {
+                        return System.getProperty(UNSAFE_SERIALIZABLE_PROPERTY);
+                    }
+                });
+        } catch (SecurityException ex) {
+            unsafeSerializableProperty = null;
+        }
+
+        if (!"true".equalsIgnoreCase(unsafeSerializableProperty)) {
+            throw new UnsupportedOperationException(
+                    "Serialization support for " + clazz.getName() + " is disabled for security reasons. " +
+                    "To enable it set system property '" + UNSAFE_SERIALIZABLE_PROPERTY + "' to 'true', " +
+                    "but you must ensure that your application does not de-serialize objects from untrusted sources.");
+        }
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/InstantiateFactory.java
+++ b/src/java/org/apache/commons/collections/functors/InstantiateFactory.java
@@ -136,5 +136,9 @@
             throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
         }
     }
-    
+
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(InstantiateFactory.class);
+        is.defaultReadObject();
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java
+++ b/src/java/org/apache/commons/collections/functors/InstantiateTransformer.java
@@ -116,4 +116,8 @@
         }
     }
 
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class);
+        is.defaultReadObject();
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/InvokerTransformer.java
+++ b/src/java/org/apache/commons/collections/functors/InvokerTransformer.java
@@ -134,4 +134,8 @@
         }
     }
 
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
+        is.defaultReadObject();
+    }
 }
--- a/src/java/org/apache/commons/collections/functors/PrototypeFactory.java
+++ b/src/java/org/apache/commons/collections/functors/PrototypeFactory.java
@@ -144,6 +144,11 @@
                 throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
             }
         }
+
+        private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
+            FunctorUtils.checkUnsafeSerialization(PrototypeCloneFactory.class);
+            is.defaultReadObject();
+        }
     }
 
     // PrototypeSerializationFactory
@@ -204,6 +209,11 @@
                 }
             }
         }
+
+        private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
+            FunctorUtils.checkUnsafeSerialization(PrototypeSerializationFactory.class);
+            is.defaultReadObject();
+        }
     }
 
 }
--- a/src/java/org/apache/commons/collections/functors/WhileClosure.java
+++ b/src/java/org/apache/commons/collections/functors/WhileClosure.java
@@ -120,4 +120,8 @@
         return iDoLoop;
     }
 
+    private void readObject(java.io.ObjectInputStream is) throws ClassNotFoundException, java.io.IOException {
+        FunctorUtils.checkUnsafeSerialization(WhileClosure.class);
+        is.defaultReadObject();
+    }
 }
