You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bd...@apache.org on 2015/11/19 18:08:23 UTC

svn commit: r1715217 - in /commons/proper/io/trunk/src: main/java/org/apache/commons/io/serialization/ test/java/org/apache/commons/io/serialization/

Author: bdelacretaz
Date: Thu Nov 19 17:08:23 2015
New Revision: 1715217

URL: http://svn.apache.org/viewvc?rev=1715217&view=rev
Log:
IO-487 - ValidatingObjectInputStream, restricts which classes can be deserialized

Added:
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java
    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java

Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java (added)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,29 @@
+/*
+ * 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
+ *
+ *   http://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;
+
+/** Match a Class name */
+public interface ClassNameMatcher {
+
+    /** True if the supplied class names matches. 
+     * @param className fully qualified class name
+     * @return true if the class name matches this object's condition
+     */
+    boolean matches(String className);
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java (added)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,44 @@
+/*
+ * 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
+ *
+ *   http://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 java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** {@link ClassNameMatcher} that matches on full class names */  
+final class FullClassNameMatcher implements ClassNameMatcher {
+
+    private final Set<String> classesSet;
+
+    /**
+     * Constructs an object based on the specified class names.
+     * 
+     * @param classes a list of class names
+     */
+    public FullClassNameMatcher(String... classes) {
+        classesSet = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(classes)));
+    }
+
+    @Override
+    public boolean matches(String className) {
+        return classesSet.contains(className);
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java (added)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,53 @@
+/*
+ * 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
+ *
+ *   http://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 java.util.regex.Pattern;
+
+/** {@link ClassNameMatcher} that uses regular expressions */  
+final class RegexpClassNameMatcher implements ClassNameMatcher {
+
+    private final Pattern pattern; // Class is thread-safe
+
+    /**
+     * Constructs an object based on the specified regular expression.
+     * 
+     * @param regex a regular expression for evaluating acceptable class names
+     */
+    public RegexpClassNameMatcher(String regex) {
+        this(Pattern.compile(regex));
+    }
+
+    /**
+     * Constructs an object based on the specified pattern.
+     * 
+     * @param pattern a pattern for evaluating acceptable class names
+     */
+    public RegexpClassNameMatcher(Pattern pattern) {
+        if(pattern == null) {
+            throw new IllegalArgumentException("Null pattern");
+        }
+        this.pattern = pattern;
+    }
+
+    @Override
+    public boolean matches(String className) {
+        return pattern.matcher(className).matches();
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java (added)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,194 @@
+/*
+ * 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
+ *
+ *   http://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 java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * An <code>ObjectInputStream</code> that's restricted to deserialize
+ * a limited set of classes.
+ * </p>
+ *
+ * <p>
+ * Various accept/reject methods allow for specifying which classes
+ * can be deserialized.
+ * </p>
+ * 
+ * <p>
+ * Design inspired by <a
+ * href="http://www.ibm.com/developerworks/library/se-lookahead/">IBM
+ * DeveloperWorks Article</a>.
+ * </p>
+ */
+public class ValidatingObjectInputStream extends ObjectInputStream {
+    private final List<ClassNameMatcher> acceptMatchers = new ArrayList<ClassNameMatcher>();
+    private final List<ClassNameMatcher> rejectMatchers = new ArrayList<ClassNameMatcher>();
+
+    /**
+     * Constructs an object to deserialize the specified input stream.
+     * At least one accept method needs to be called to specify which
+     * classes can be deserialized, as by default no classes are 
+     * accepted. 
+     * 
+     * @param input an input stream
+     * @param acceptor a class acceptor
+     * @throws IOException if an I/O error occurs while reading stream header
+     */
+    public ValidatingObjectInputStream(InputStream input) throws IOException {
+        super(input);
+    }
+
+    private void validateClassName(String name) throws InvalidClassException {
+        // Reject has precedence over accept
+        for(ClassNameMatcher m : rejectMatchers) {
+            if(m.matches(name)) {
+                invalidClassNameFound(name);
+            }
+        }
+        
+        boolean ok = false;
+        for(ClassNameMatcher m : acceptMatchers) {
+            if(m.matches(name)) {
+                ok = true;
+                break;
+            }
+        }
+        if(!ok) {
+            invalidClassNameFound(name);
+        }
+    }
+    
+    /** Called to throw InvalidClassException (by default) if an invalid
+     *  class name is found in deserialization. Can be overridden, for example
+     *  to log those class names.
+     *  By default the name of the invalid class is not included in the
+     *  exception thrown, as that might give too much information from a
+     *  security point of view.
+     *  
+     * @param className name of the invalid class 
+     * @throws InvalidClassException
+     */
+    protected void invalidClassNameFound(String className) throws InvalidClassException{
+        throw new InvalidClassException("Class name not accepted");
+    }
+
+    @Override
+    protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
+        validateClassName(osc.getName());
+        return super.resolveClass(osc);
+    }
+
+    /** Accept the specified classes for deserialization, unless they 
+     *  are otherwise rejected.
+     * @param classes Classes to accept
+     * @return this object
+     */
+    public ValidatingObjectInputStream accept(Class<?>... classes) {
+        for(Class<?> c : classes) {
+            acceptMatchers.add(new FullClassNameMatcher(c.getName()));
+        }
+         return this;
+    }
+
+    /** Reject the specified classes for deserialization, even if they 
+     *  are otherwise accepted.
+     * @param classes Classes to reject
+     * @return this object
+     */
+    public ValidatingObjectInputStream reject(Class<?>... classes) {
+        for(Class<?> c : classes) {
+            rejectMatchers.add(new FullClassNameMatcher(c.getName()));
+        }
+        return this;
+    }
+
+    /** Accept the wildcard specified classes for deserialization, 
+     *  unless they are otherwise rejected.
+     * @param patterns Wildcard filename patterns as defined by
+     *                  {@link FilenameUtils.wildcardMatch}
+     * @return this object
+     */
+    public ValidatingObjectInputStream accept(String ... patterns) {
+        for(String pattern : patterns) {
+            acceptMatchers.add(new WildcardClassNameMatcher(pattern));
+        }
+        return this;
+    }
+
+    /** Reject the wildcard specified classes for deserialization, 
+     *  even if they are otherwise accepted.
+     * @param patterns Wildcard filename patterns as defined by
+     *                  {@link FilenameUtils.wildcardMatch}
+     * @return this object
+     */
+    public ValidatingObjectInputStream reject(String ... patterns) {
+        for(String pattern : patterns) {
+            rejectMatchers.add(new WildcardClassNameMatcher(pattern));
+        }
+        return this;
+    }
+
+    /** Accept class names that match the supplied pattern for
+     *  deserialization, unless they are otherwise rejected.
+     * @param pattern standard Java regexp
+     * @return this object
+     */
+    public ValidatingObjectInputStream accept(Pattern pattern) {
+        acceptMatchers.add(new RegexpClassNameMatcher(pattern));
+        return this;
+    }
+
+    /** Reject class names that match the supplied pattern for
+     *  deserialization, even if they are otherwise accepted.
+     * @param pattern standard Java regexp
+     * @return this object
+     */
+    public ValidatingObjectInputStream reject(Pattern pattern) {
+        rejectMatchers.add(new RegexpClassNameMatcher(pattern));
+        return this;
+    }
+
+    /** Accept class names where the supplied ClassNameMatcher matches for
+     *  deserialization, unless they are otherwise rejected.
+     * @param m the matcher to use
+     * @return this object
+     */
+    public ValidatingObjectInputStream accept(ClassNameMatcher m) {
+        acceptMatchers.add(m);
+        return this;
+    }
+
+    /** Reject class names where the supplied ClassNameMatcher matches for
+     *  deserialization, even if they are otherwise accepted.
+     * @param m the matcher to use
+     * @return this object
+     */
+    public ValidatingObjectInputStream reject(ClassNameMatcher m) {
+        rejectMatchers.add(m);
+        return this;
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java (added)
+++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,41 @@
+/*
+ * 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
+ *
+ *   http://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 org.apache.commons.io.FilenameUtils;
+
+/** {@link ClassNameMatcher} that uses simplified regular expressions
+ *  provided by {@link FilenameUtils#wildcardMatch} */  
+final class WildcardClassNameMatcher implements ClassNameMatcher {
+
+    private final String pattern;
+
+    /**
+     * Constructs an object based on the specified simplified regular expression.
+     * @param regex a {@link FilenameUtils#wildcardMatch} pattern.
+     */
+    public WildcardClassNameMatcher(String pattern) {
+        this.pattern = pattern;
+    }
+    
+    @Override
+    public boolean matches(String className) {
+        return FilenameUtils.wildcardMatch(className, pattern);
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java (added)
+++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ *   http://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.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class FullClassNameMatcherTest {
+
+    private static final String [] NAMES_ARRAY = { Integer.class.getName(), Long.class.getName() };
+
+    @Test
+    public void noNames() throws Exception {
+        final FullClassNameMatcher m = new FullClassNameMatcher();
+        assertFalse(m.matches(Integer.class.getName()));
+    }
+
+    @Test
+    public void withNames() throws Exception {
+        final FullClassNameMatcher m = new FullClassNameMatcher(NAMES_ARRAY);
+        assertTrue(m.matches(Integer.class.getName()));
+        assertFalse(m.matches(String.class.getName()));
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java (added)
+++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,44 @@
+/*
+ * 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
+ *
+ *   http://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 java.io.Serializable;
+
+public class OurTestClass implements Serializable {
+    private static final long serialVersionUID = 2139985988735372175L;
+    
+    private final String str;
+    
+    OurTestClass(String str) {
+        this.str = str;
+    }
+
+    @Override
+    public int hashCode() {
+        return str.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(!(obj instanceof OurTestClass)) {
+            return false;
+        }
+        return str.equals(((OurTestClass)obj).str);
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java (added)
+++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,61 @@
+/*
+ * 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
+ *
+ *   http://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.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+public class RegexpClassNameMatcherTest {
+
+    @Test
+    public void testSimplePatternFromString() {
+        ClassNameMatcher ca = new RegexpClassNameMatcher("foo.*");
+        assertTrue(ca.matches("foo.should.match"));
+        assertFalse(ca.matches("bar.should.not.match"));
+    }
+
+    @Test
+    public void testSimplePatternFromPattern() {
+        ClassNameMatcher ca = new RegexpClassNameMatcher(Pattern.compile("foo.*"));
+        assertTrue(ca.matches("foo.should.match"));
+        assertFalse(ca.matches("bar.should.not.match"));
+    }
+
+    @Test
+    public void testOrPattern() {
+        ClassNameMatcher ca = new RegexpClassNameMatcher("foo.*|bar.*");
+        assertTrue(ca.matches("foo.should.match"));
+        assertTrue(ca.matches("bar.should.match"));
+        assertFalse(ca.matches("zoo.should.not.match"));
+    }
+
+    @Test(expected=NullPointerException.class)
+    public void testNullStringPattern() {
+        new RegexpClassNameMatcher((String)null);
+    }
+    
+    @Test(expected=IllegalArgumentException.class)
+    public void testNullPatternPattern() {
+        new RegexpClassNameMatcher((Pattern)null);
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java (added)
+++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,231 @@
+/*
+ * 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
+ *
+ *   http://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.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ValidatingObjectInputStreamTest {
+    private List<Closeable> toClose;
+    private OurTestClass testObject;
+    private InputStream testStream;
+
+    static private final ClassNameMatcher ALWAYS_TRUE = new ClassNameMatcher() {
+        @Override
+        public boolean matches(String className) {
+            return true;
+        }
+    };
+
+    private <T extends Closeable> T willClose(T t) {
+        toClose.add(t);
+        return t;
+    }
+
+    @Before
+    public void setup() throws IOException {
+        toClose = new ArrayList<Closeable>();
+        testObject = new OurTestClass(UUID.randomUUID().toString());
+        final ByteArrayOutputStream bos = willClose(new ByteArrayOutputStream());
+        final ObjectOutputStream oos = willClose(new ObjectOutputStream(bos));
+        oos.writeObject(testObject);
+        testStream = willClose(new ByteArrayInputStream(bos.toByteArray()));
+    }
+
+    @After
+    public void cleanup() {
+        for (Closeable c : toClose) {
+            try {
+                c.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+    private void assertSerialization(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+        final OurTestClass result = (OurTestClass) (ois.readObject());
+        assertEquals(testObject, result);
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void noAccept() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream)));
+    }
+
+    @Test
+    public void acceptCustomMatcher() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(ALWAYS_TRUE)
+        );
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void rejectCustomMatcher() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class)
+                .reject(ALWAYS_TRUE)
+        );
+    }
+
+    @Test
+    public void acceptPattern() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(Pattern.compile(".*OurTestClass.*"))
+        );
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void rejectPattern() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class)
+                .reject(Pattern.compile("org.*"))
+        );
+    }
+
+    @Test
+    public void acceptWildcard() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept("org.apache.commons.io.*")
+        );
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void rejectWildcard() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class)
+                .reject("org.*")
+        );
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void ourTestClassNotAccepted() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(Integer.class)
+        );
+    }
+
+    @Test
+    public void ourTestClassOnlyAccepted() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class)
+        );
+    }
+
+    @Test
+    public void ourTestClassAcceptedFirst() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class, Integer.class)
+        );
+    }
+
+    @Test
+    public void ourTestClassAcceptedSecond() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(Integer.class, OurTestClass.class)
+        );
+    }
+
+    @Test
+    public void ourTestClassAcceptedFirstWildcard() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept("*OurTestClass","*Integer")
+        );
+    }
+
+    @Test
+    public void ourTestClassAcceptedSecondWildcard() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept("*Integer","*OurTestClass")
+        );
+    }
+
+    @Test(expected = InvalidClassException.class)
+    public void reject() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(Long.class)
+                .reject(OurTestClass.class, Integer.class)
+        );
+    }
+    
+    @Test(expected = InvalidClassException.class)
+    public void rejectPrecedence() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .accept(OurTestClass.class)
+                .reject(OurTestClass.class, Integer.class)
+        );
+    }
+    
+    @Test(expected = InvalidClassException.class)
+    public void rejectOnly() throws Exception {
+        assertSerialization(
+                willClose(new ValidatingObjectInputStream(testStream))
+                .reject(Integer.class)
+        );
+    }
+    
+    @Test(expected = RuntimeException.class)
+    public void customInvalidMethod() throws Exception {
+        class CustomVOIS extends ValidatingObjectInputStream {
+            CustomVOIS(InputStream is) throws IOException {
+                super(is);
+            }
+
+            @Override
+            protected void invalidClassNameFound(String className) throws InvalidClassException {
+                throw new RuntimeException("Custom exception");
+            }
+        };
+        
+        assertSerialization(
+                willClose(new CustomVOIS(testStream))
+                .reject(Integer.class)
+        );
+    }
+}
\ No newline at end of file

Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java?rev=1715217&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java (added)
+++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
@@ -0,0 +1,50 @@
+/*
+ * 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
+ *
+ *   http://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.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class WildcardClassNameMatcherTest {
+
+    @Test
+    public void noPattern() {
+        ClassNameMatcher ca = new WildcardClassNameMatcher("org.foo");
+        assertTrue(ca.matches("org.foo"));
+        assertFalse(ca.matches("org.foo.and.more"));
+        assertFalse(ca.matches("org_foo"));
+    }
+
+    @Test
+    public void star() {
+        ClassNameMatcher ca = new WildcardClassNameMatcher("org*");
+        assertTrue(ca.matches("org.foo.should.match"));
+        assertFalse(ca.matches("bar.should.not.match"));
+    }
+
+    @Test
+    public void starAndQuestionMark() {
+        ClassNameMatcher ca = new WildcardClassNameMatcher("org?apache?something*");
+        assertTrue(ca.matches("org.apache_something.more"));
+        assertFalse(ca.matches("org..apache_something.more"));
+    }
+
+}
\ No newline at end of file



Re: svn commit: r1715217 - in /commons/proper/io/trunk/src: main/java/org/apache/commons/io/serialization/ test/java/org/apache/commons/io/serialization/

Posted by Bertrand Delacretaz <bd...@apache.org>.
On Thu, Nov 19, 2015 at 2:26 PM, Benedikt Ritter <be...@gmail.com> wrote:
> +1
> Very nice to see this happening :-)

Thanks, and thanks to all the contributors to IO-487!

-Bertrand

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r1715217 - in /commons/proper/io/trunk/src: main/java/org/apache/commons/io/serialization/ test/java/org/apache/commons/io/serialization/

Posted by Benedikt Ritter <be...@gmail.com>.
+1

Very nice to see this happening :-)

Send from my mobile device

> Am 19.11.2015 um 18:08 schrieb bdelacretaz@apache.org:
> 
> Author: bdelacretaz
> Date: Thu Nov 19 17:08:23 2015
> New Revision: 1715217
> 
> URL: http://svn.apache.org/viewvc?rev=1715217&view=rev
> Log:
> IO-487 - ValidatingObjectInputStream, restricts which classes can be deserialized
> 
> Added:
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
>    commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java
>    commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java
> 
> Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java (added)
> +++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ClassNameMatcher.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,29 @@
> +/*
> + * 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
> + *
> + *   http://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;
> +
> +/** Match a Class name */
> +public interface ClassNameMatcher {
> +
> +    /** True if the supplied class names matches. 
> +     * @param className fully qualified class name
> +     * @return true if the class name matches this object's condition
> +     */
> +    boolean matches(String className);
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java (added)
> +++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/FullClassNameMatcher.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,44 @@
> +/*
> + * 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
> + *
> + *   http://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 java.util.Arrays;
> +import java.util.Collections;
> +import java.util.HashSet;
> +import java.util.Set;
> +
> +/** {@link ClassNameMatcher} that matches on full class names */  
> +final class FullClassNameMatcher implements ClassNameMatcher {
> +
> +    private final Set<String> classesSet;
> +
> +    /**
> +     * Constructs an object based on the specified class names.
> +     * 
> +     * @param classes a list of class names
> +     */
> +    public FullClassNameMatcher(String... classes) {
> +        classesSet = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(classes)));
> +    }
> +
> +    @Override
> +    public boolean matches(String className) {
> +        return classesSet.contains(className);
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java (added)
> +++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,53 @@
> +/*
> + * 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
> + *
> + *   http://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 java.util.regex.Pattern;
> +
> +/** {@link ClassNameMatcher} that uses regular expressions */  
> +final class RegexpClassNameMatcher implements ClassNameMatcher {
> +
> +    private final Pattern pattern; // Class is thread-safe
> +
> +    /**
> +     * Constructs an object based on the specified regular expression.
> +     * 
> +     * @param regex a regular expression for evaluating acceptable class names
> +     */
> +    public RegexpClassNameMatcher(String regex) {
> +        this(Pattern.compile(regex));
> +    }
> +
> +    /**
> +     * Constructs an object based on the specified pattern.
> +     * 
> +     * @param pattern a pattern for evaluating acceptable class names
> +     */
> +    public RegexpClassNameMatcher(Pattern pattern) {
> +        if(pattern == null) {
> +            throw new IllegalArgumentException("Null pattern");
> +        }
> +        this.pattern = pattern;
> +    }
> +
> +    @Override
> +    public boolean matches(String className) {
> +        return pattern.matcher(className).matches();
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java (added)
> +++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,194 @@
> +/*
> + * 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
> + *
> + *   http://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 java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InvalidClassException;
> +import java.io.ObjectInputStream;
> +import java.io.ObjectStreamClass;
> +import java.util.ArrayList;
> +import java.util.List;
> +import java.util.regex.Pattern;
> +
> +/**
> + * <p>
> + * An <code>ObjectInputStream</code> that's restricted to deserialize
> + * a limited set of classes.
> + * </p>
> + *
> + * <p>
> + * Various accept/reject methods allow for specifying which classes
> + * can be deserialized.
> + * </p>
> + * 
> + * <p>
> + * Design inspired by <a
> + * href="http://www.ibm.com/developerworks/library/se-lookahead/">IBM
> + * DeveloperWorks Article</a>.
> + * </p>
> + */
> +public class ValidatingObjectInputStream extends ObjectInputStream {
> +    private final List<ClassNameMatcher> acceptMatchers = new ArrayList<ClassNameMatcher>();
> +    private final List<ClassNameMatcher> rejectMatchers = new ArrayList<ClassNameMatcher>();
> +
> +    /**
> +     * Constructs an object to deserialize the specified input stream.
> +     * At least one accept method needs to be called to specify which
> +     * classes can be deserialized, as by default no classes are 
> +     * accepted. 
> +     * 
> +     * @param input an input stream
> +     * @param acceptor a class acceptor
> +     * @throws IOException if an I/O error occurs while reading stream header
> +     */
> +    public ValidatingObjectInputStream(InputStream input) throws IOException {
> +        super(input);
> +    }
> +
> +    private void validateClassName(String name) throws InvalidClassException {
> +        // Reject has precedence over accept
> +        for(ClassNameMatcher m : rejectMatchers) {
> +            if(m.matches(name)) {
> +                invalidClassNameFound(name);
> +            }
> +        }
> +        
> +        boolean ok = false;
> +        for(ClassNameMatcher m : acceptMatchers) {
> +            if(m.matches(name)) {
> +                ok = true;
> +                break;
> +            }
> +        }
> +        if(!ok) {
> +            invalidClassNameFound(name);
> +        }
> +    }
> +    
> +    /** Called to throw InvalidClassException (by default) if an invalid
> +     *  class name is found in deserialization. Can be overridden, for example
> +     *  to log those class names.
> +     *  By default the name of the invalid class is not included in the
> +     *  exception thrown, as that might give too much information from a
> +     *  security point of view.
> +     *  
> +     * @param className name of the invalid class 
> +     * @throws InvalidClassException
> +     */
> +    protected void invalidClassNameFound(String className) throws InvalidClassException{
> +        throw new InvalidClassException("Class name not accepted");
> +    }
> +
> +    @Override
> +    protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
> +        validateClassName(osc.getName());
> +        return super.resolveClass(osc);
> +    }
> +
> +    /** Accept the specified classes for deserialization, unless they 
> +     *  are otherwise rejected.
> +     * @param classes Classes to accept
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream accept(Class<?>... classes) {
> +        for(Class<?> c : classes) {
> +            acceptMatchers.add(new FullClassNameMatcher(c.getName()));
> +        }
> +         return this;
> +    }
> +
> +    /** Reject the specified classes for deserialization, even if they 
> +     *  are otherwise accepted.
> +     * @param classes Classes to reject
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream reject(Class<?>... classes) {
> +        for(Class<?> c : classes) {
> +            rejectMatchers.add(new FullClassNameMatcher(c.getName()));
> +        }
> +        return this;
> +    }
> +
> +    /** Accept the wildcard specified classes for deserialization, 
> +     *  unless they are otherwise rejected.
> +     * @param patterns Wildcard filename patterns as defined by
> +     *                  {@link FilenameUtils.wildcardMatch}
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream accept(String ... patterns) {
> +        for(String pattern : patterns) {
> +            acceptMatchers.add(new WildcardClassNameMatcher(pattern));
> +        }
> +        return this;
> +    }
> +
> +    /** Reject the wildcard specified classes for deserialization, 
> +     *  even if they are otherwise accepted.
> +     * @param patterns Wildcard filename patterns as defined by
> +     *                  {@link FilenameUtils.wildcardMatch}
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream reject(String ... patterns) {
> +        for(String pattern : patterns) {
> +            rejectMatchers.add(new WildcardClassNameMatcher(pattern));
> +        }
> +        return this;
> +    }
> +
> +    /** Accept class names that match the supplied pattern for
> +     *  deserialization, unless they are otherwise rejected.
> +     * @param pattern standard Java regexp
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream accept(Pattern pattern) {
> +        acceptMatchers.add(new RegexpClassNameMatcher(pattern));
> +        return this;
> +    }
> +
> +    /** Reject class names that match the supplied pattern for
> +     *  deserialization, even if they are otherwise accepted.
> +     * @param pattern standard Java regexp
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream reject(Pattern pattern) {
> +        rejectMatchers.add(new RegexpClassNameMatcher(pattern));
> +        return this;
> +    }
> +
> +    /** Accept class names where the supplied ClassNameMatcher matches for
> +     *  deserialization, unless they are otherwise rejected.
> +     * @param m the matcher to use
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream accept(ClassNameMatcher m) {
> +        acceptMatchers.add(m);
> +        return this;
> +    }
> +
> +    /** Reject class names where the supplied ClassNameMatcher matches for
> +     *  deserialization, even if they are otherwise accepted.
> +     * @param m the matcher to use
> +     * @return this object
> +     */
> +    public ValidatingObjectInputStream reject(ClassNameMatcher m) {
> +        rejectMatchers.add(m);
> +        return this;
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java (added)
> +++ commons/proper/io/trunk/src/main/java/org/apache/commons/io/serialization/WildcardClassNameMatcher.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,41 @@
> +/*
> + * 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
> + *
> + *   http://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 org.apache.commons.io.FilenameUtils;
> +
> +/** {@link ClassNameMatcher} that uses simplified regular expressions
> + *  provided by {@link FilenameUtils#wildcardMatch} */  
> +final class WildcardClassNameMatcher implements ClassNameMatcher {
> +
> +    private final String pattern;
> +
> +    /**
> +     * Constructs an object based on the specified simplified regular expression.
> +     * @param regex a {@link FilenameUtils#wildcardMatch} pattern.
> +     */
> +    public WildcardClassNameMatcher(String pattern) {
> +        this.pattern = pattern;
> +    }
> +    
> +    @Override
> +    public boolean matches(String className) {
> +        return FilenameUtils.wildcardMatch(className, pattern);
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java (added)
> +++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/FullClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,42 @@
> +/*
> + * 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
> + *
> + *   http://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.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +import org.junit.Test;
> +
> +public class FullClassNameMatcherTest {
> +
> +    private static final String [] NAMES_ARRAY = { Integer.class.getName(), Long.class.getName() };
> +
> +    @Test
> +    public void noNames() throws Exception {
> +        final FullClassNameMatcher m = new FullClassNameMatcher();
> +        assertFalse(m.matches(Integer.class.getName()));
> +    }
> +
> +    @Test
> +    public void withNames() throws Exception {
> +        final FullClassNameMatcher m = new FullClassNameMatcher(NAMES_ARRAY);
> +        assertTrue(m.matches(Integer.class.getName()));
> +        assertFalse(m.matches(String.class.getName()));
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java (added)
> +++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/OurTestClass.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,44 @@
> +/*
> + * 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
> + *
> + *   http://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 java.io.Serializable;
> +
> +public class OurTestClass implements Serializable {
> +    private static final long serialVersionUID = 2139985988735372175L;
> +    
> +    private final String str;
> +    
> +    OurTestClass(String str) {
> +        this.str = str;
> +    }
> +
> +    @Override
> +    public int hashCode() {
> +        return str.hashCode();
> +    }
> +
> +    @Override
> +    public boolean equals(Object obj) {
> +        if(!(obj instanceof OurTestClass)) {
> +            return false;
> +        }
> +        return str.equals(((OurTestClass)obj).str);
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java (added)
> +++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/RegexpClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,61 @@
> +/*
> + * 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
> + *
> + *   http://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.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +import java.util.regex.Pattern;
> +
> +import org.junit.Test;
> +
> +public class RegexpClassNameMatcherTest {
> +
> +    @Test
> +    public void testSimplePatternFromString() {
> +        ClassNameMatcher ca = new RegexpClassNameMatcher("foo.*");
> +        assertTrue(ca.matches("foo.should.match"));
> +        assertFalse(ca.matches("bar.should.not.match"));
> +    }
> +
> +    @Test
> +    public void testSimplePatternFromPattern() {
> +        ClassNameMatcher ca = new RegexpClassNameMatcher(Pattern.compile("foo.*"));
> +        assertTrue(ca.matches("foo.should.match"));
> +        assertFalse(ca.matches("bar.should.not.match"));
> +    }
> +
> +    @Test
> +    public void testOrPattern() {
> +        ClassNameMatcher ca = new RegexpClassNameMatcher("foo.*|bar.*");
> +        assertTrue(ca.matches("foo.should.match"));
> +        assertTrue(ca.matches("bar.should.match"));
> +        assertFalse(ca.matches("zoo.should.not.match"));
> +    }
> +
> +    @Test(expected=NullPointerException.class)
> +    public void testNullStringPattern() {
> +        new RegexpClassNameMatcher((String)null);
> +    }
> +    
> +    @Test(expected=IllegalArgumentException.class)
> +    public void testNullPatternPattern() {
> +        new RegexpClassNameMatcher((Pattern)null);
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java (added)
> +++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/ValidatingObjectInputStreamTest.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,231 @@
> +/*
> + * 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
> + *
> + *   http://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.Assert.assertEquals;
> +
> +import java.io.ByteArrayInputStream;
> +import java.io.ByteArrayOutputStream;
> +import java.io.Closeable;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InvalidClassException;
> +import java.io.ObjectInputStream;
> +import java.io.ObjectOutputStream;
> +import java.util.ArrayList;
> +import java.util.List;
> +import java.util.UUID;
> +import java.util.regex.Pattern;
> +
> +import org.junit.After;
> +import org.junit.Before;
> +import org.junit.Test;
> +
> +public class ValidatingObjectInputStreamTest {
> +    private List<Closeable> toClose;
> +    private OurTestClass testObject;
> +    private InputStream testStream;
> +
> +    static private final ClassNameMatcher ALWAYS_TRUE = new ClassNameMatcher() {
> +        @Override
> +        public boolean matches(String className) {
> +            return true;
> +        }
> +    };
> +
> +    private <T extends Closeable> T willClose(T t) {
> +        toClose.add(t);
> +        return t;
> +    }
> +
> +    @Before
> +    public void setup() throws IOException {
> +        toClose = new ArrayList<Closeable>();
> +        testObject = new OurTestClass(UUID.randomUUID().toString());
> +        final ByteArrayOutputStream bos = willClose(new ByteArrayOutputStream());
> +        final ObjectOutputStream oos = willClose(new ObjectOutputStream(bos));
> +        oos.writeObject(testObject);
> +        testStream = willClose(new ByteArrayInputStream(bos.toByteArray()));
> +    }
> +
> +    @After
> +    public void cleanup() {
> +        for (Closeable c : toClose) {
> +            try {
> +                c.close();
> +            } catch (IOException ignored) {
> +            }
> +        }
> +    }
> +
> +    private void assertSerialization(ObjectInputStream ois) throws ClassNotFoundException, IOException {
> +        final OurTestClass result = (OurTestClass) (ois.readObject());
> +        assertEquals(testObject, result);
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void noAccept() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream)));
> +    }
> +
> +    @Test
> +    public void acceptCustomMatcher() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(ALWAYS_TRUE)
> +        );
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void rejectCustomMatcher() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class)
> +                .reject(ALWAYS_TRUE)
> +        );
> +    }
> +
> +    @Test
> +    public void acceptPattern() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(Pattern.compile(".*OurTestClass.*"))
> +        );
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void rejectPattern() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class)
> +                .reject(Pattern.compile("org.*"))
> +        );
> +    }
> +
> +    @Test
> +    public void acceptWildcard() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept("org.apache.commons.io.*")
> +        );
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void rejectWildcard() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class)
> +                .reject("org.*")
> +        );
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void ourTestClassNotAccepted() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(Integer.class)
> +        );
> +    }
> +
> +    @Test
> +    public void ourTestClassOnlyAccepted() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class)
> +        );
> +    }
> +
> +    @Test
> +    public void ourTestClassAcceptedFirst() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class, Integer.class)
> +        );
> +    }
> +
> +    @Test
> +    public void ourTestClassAcceptedSecond() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(Integer.class, OurTestClass.class)
> +        );
> +    }
> +
> +    @Test
> +    public void ourTestClassAcceptedFirstWildcard() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept("*OurTestClass","*Integer")
> +        );
> +    }
> +
> +    @Test
> +    public void ourTestClassAcceptedSecondWildcard() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept("*Integer","*OurTestClass")
> +        );
> +    }
> +
> +    @Test(expected = InvalidClassException.class)
> +    public void reject() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(Long.class)
> +                .reject(OurTestClass.class, Integer.class)
> +        );
> +    }
> +    
> +    @Test(expected = InvalidClassException.class)
> +    public void rejectPrecedence() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .accept(OurTestClass.class)
> +                .reject(OurTestClass.class, Integer.class)
> +        );
> +    }
> +    
> +    @Test(expected = InvalidClassException.class)
> +    public void rejectOnly() throws Exception {
> +        assertSerialization(
> +                willClose(new ValidatingObjectInputStream(testStream))
> +                .reject(Integer.class)
> +        );
> +    }
> +    
> +    @Test(expected = RuntimeException.class)
> +    public void customInvalidMethod() throws Exception {
> +        class CustomVOIS extends ValidatingObjectInputStream {
> +            CustomVOIS(InputStream is) throws IOException {
> +                super(is);
> +            }
> +
> +            @Override
> +            protected void invalidClassNameFound(String className) throws InvalidClassException {
> +                throw new RuntimeException("Custom exception");
> +            }
> +        };
> +        
> +        assertSerialization(
> +                willClose(new CustomVOIS(testStream))
> +                .reject(Integer.class)
> +        );
> +    }
> +}
> \ No newline at end of file
> 
> Added: commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java
> URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java?rev=1715217&view=auto
> ==============================================================================
> --- commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java (added)
> +++ commons/proper/io/trunk/src/test/java/org/apache/commons/io/serialization/WildcardClassNameMatcherTest.java Thu Nov 19 17:08:23 2015
> @@ -0,0 +1,50 @@
> +/*
> + * 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
> + *
> + *   http://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.Assert.assertFalse;
> +import static org.junit.Assert.assertTrue;
> +
> +import org.junit.Test;
> +
> +public class WildcardClassNameMatcherTest {
> +
> +    @Test
> +    public void noPattern() {
> +        ClassNameMatcher ca = new WildcardClassNameMatcher("org.foo");
> +        assertTrue(ca.matches("org.foo"));
> +        assertFalse(ca.matches("org.foo.and.more"));
> +        assertFalse(ca.matches("org_foo"));
> +    }
> +
> +    @Test
> +    public void star() {
> +        ClassNameMatcher ca = new WildcardClassNameMatcher("org*");
> +        assertTrue(ca.matches("org.foo.should.match"));
> +        assertFalse(ca.matches("bar.should.not.match"));
> +    }
> +
> +    @Test
> +    public void starAndQuestionMark() {
> +        ClassNameMatcher ca = new WildcardClassNameMatcher("org?apache?something*");
> +        assertTrue(ca.matches("org.apache_something.more"));
> +        assertFalse(ca.matches("org..apache_something.more"));
> +    }
> +
> +}
> \ No newline at end of file
> 
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org