Package org.apache.commons.io.serialization

# Class ValidatingObjectInputStream

java.lang.Object
java.io.InputStream
java.io.ObjectInputStream
org.apache.commons.io.serialization.ValidatingObjectInputStream

All Implemented Interfaces:
  `Closeable`, `DataInput`, `ObjectInput`, `ObjectStreamConstants`, `AutoCloseable`

public class ValidatingObjectInputStream extends ObjectInputStream
An `ObjectInputStream` that's restricted to deserialize a limited set of classes.

Various accept/reject methods allow for specifying which classes can be deserialized.

## Deserlizing safely

Here is the only way to safely read a HashMap of String keys and Integer values:

     // Defining Object fixture
     final HashMap<String, Integer> map1 = new HashMap<>();
     map1.put("1", 1);
     // Writing serialized fixture
     final byte[] byteArray;
     try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             final ObjectOutputStream oos = new ObjectOutputStream(baos)) {
         oos.writeObject(map1);
         oos.flush();
         byteArray = baos.toByteArray();
     }
     // Deserializing
     try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
             ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
                 .accept(HashMap.class, Number.class, Integer.class)
                 .setInputStream(bais)
                 .get()) {
         // String.class is automatically accepted
         final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
         assertEquals(map1, map2);
     }
     // Reusing a configuration
     final ObjectStreamClassPredicate predicate = new ObjectStreamClassPredicate()
         .accept(HashMap.class, Number.class, Integer.class);
     try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
             ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
                 .setPredicate(predicate)
                 .setInputStream(bais)
                 .get()) {
         // String.class is automatically accepted
         final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
         assertEquals(map1, map2);
     }

This design was inspired by a IBM DeveloperWorks Article.

## Deserlizing with a size boundary

You can further guard your application againt untrusted input by limiting how much data to process using a `BoundedInputStream`. For example:

     // Deserializing with a size limit successfully
     try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
             ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
                 .accept(HashMap.class, Number.class, Integer.class)
                 .setInputStream(BoundedInputStream.builder()
                     .setMaxCount(10_000)
                     .setOnMaxCount((max, count) -> {
                         throw new IllegalArgumentException("Input exceeds limit.");
                     })
                     .setInputStream(bais)
                     .get())
                 .get()) {
         // String.class is automatically accepted
         final HashMap<String, Integer> map2 = (HashMap<String, Integer>) vois.readObject();
         assertEquals(map1, map2);
     }
     // Deserializing with a size limit reaching the limit
     try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
             ValidatingObjectInputStream vois = ValidatingObjectInputStream.builder()
                 .accept(HashMap.class, Number.class, Integer.class)
                 .setInputStream(BoundedInputStream.builder()
                     .setMaxCount(10)
                     .setOnMaxCount((max, count) -> {
                         throw new IllegalArgumentException("Input exceeds limit.");
                     })
                     .setInputStream(bais)
                     .get())
                 .get()) {
         // String.class is automatically accepted
         final IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> vois.readObject());
         assertEquals("Input exceeds limit.", e.getMessage());
     }

Since:
  2.5

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static class`
  `ValidatingObjectInputStream.Builder`
  Builds a new `ValidatingObjectInputStream`.

  ## Nested classes/interfaces inherited from class java.io.ObjectInputStream

  `ObjectInputStream.GetField`

- ## Field Summary

  ### Fields inherited from interface java.io.ObjectStreamConstants

  `baseWireHandle, PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, SC_BLOCK_DATA, SC_ENUM, SC_EXTERNALIZABLE, SC_SERIALIZABLE, SC_WRITE_METHOD, STREAM_MAGIC, STREAM_VERSION, SUBCLASS_IMPLEMENTATION_PERMISSION, SUBSTITUTION_PERMISSION, TC_ARRAY, TC_BASE, TC_BLOCKDATA, TC_BLOCKDATALONG, TC_CLASS, TC_CLASSDESC, TC_ENDBLOCKDATA, TC_ENUM, TC_EXCEPTION, TC_LONGSTRING, TC_MAX, TC_NULL, TC_OBJECT, TC_PROXYCLASSDESC, TC_REFERENCE, TC_RESET, TC_STRING`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `ValidatingObjectInputStream(InputStream input)`
  Deprecated.
  Use `builder()`.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `ValidatingObjectInputStream`
  `accept(Class<?>... classes)`
  Accepts the specified classes for deserialization, unless they are otherwise rejected.
  `ValidatingObjectInputStream`
  `accept(String... patterns)`
  Accepts the wildcard specified classes for deserialization, unless they are otherwise rejected.
  `ValidatingObjectInputStream`
  `accept(Pattern pattern)`
  Accepts class names that match the supplied pattern for deserialization, unless they are otherwise rejected.
  `ValidatingObjectInputStream`
  `accept(ClassNameMatcher matcher)`
  Accepts class names where the supplied ClassNameMatcher matches for deserialization, unless they are otherwise rejected.
  `static ValidatingObjectInputStream.Builder`
  `builder()`
  Constructs a new `ValidatingObjectInputStream.Builder`.
  `protected void`
  `invalidClassNameFound(String className)`
  Called to throw `InvalidClassException` if an invalid class name is found during deserialization.
  `<T> T`
  `readObjectCast()`
  Delegates to `ObjectInputStream.readObject()` and casts to the generic `T`.
  `ValidatingObjectInputStream`
  `reject(Class<?>... classes)`
  Rejects the specified classes for deserialization, even if they are otherwise accepted.
  `ValidatingObjectInputStream`
  `reject(String... patterns)`
  Rejects the wildcard specified classes for deserialization, even if they are otherwise accepted.
  `ValidatingObjectInputStream`
  `reject(Pattern pattern)`
  Rejects class names that match the supplied pattern for deserialization, even if they are otherwise accepted.
  `ValidatingObjectInputStream`
  `reject(ClassNameMatcher matcher)`
  Rejects class names where the supplied ClassNameMatcher matches for deserialization, even if they are otherwise accepted.
  `protected Class<?>`
  `resolveClass(ObjectStreamClass osc)`
  Checks that the given object's class name conforms to requirements and if so delegates to the superclass.
  `protected Class<?>`
  `resolveProxyClass(String[] interfaces)`
  Checks that the given names conform to requirements and if so delegates to the superclass.

  ### Methods inherited from class java.io.ObjectInputStream

  `available, close, defaultReadObject, enableResolveObject, read, read, readBoolean, readByte, readChar, readClassDescriptor, readDouble, readFields, readFloat, readFully, readFully, readInt, readLine, readLong, readObject, readObjectOverride, readShort, readStreamHeader, readUnshared, readUnsignedByte, readUnsignedShort, readUTF, registerValidation, resolveObject, skipBytes`

  ### Methods inherited from class java.io.InputStream

  `mark, markSupported, read, reset, skip`

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

  ### Methods inherited from interface java.io.ObjectInput

  `read, skip`

- ## Constructor Details

  - ### ValidatingObjectInputStream

    @Deprecated public ValidatingObjectInputStream(InputStream input) throws IOException
    Deprecated.
    Use `builder()`.
    Constructs an instance 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.
    Parameters:
      `input` - an input stream.
    Throws:
      `IOException` - if an I/O error occurs while reading stream header.

- ## Method Details

  - ### builder

    public static ValidatingObjectInputStream.Builder builder()
    Constructs a new `ValidatingObjectInputStream.Builder`.
    Returns:
      a new `ValidatingObjectInputStream.Builder`.
    Since:
      2.18.0

  - ### accept

    public ValidatingObjectInputStream accept(Class<?>... classes)
    Accepts the specified classes for deserialization, unless they are otherwise rejected.

    The reject list takes precedence over the accept list.

    Parameters:
      `classes` - Classes to accept.
    Returns:
      `this` instance.

  - ### accept

    public ValidatingObjectInputStream accept(ClassNameMatcher matcher)
    Accepts class names where the supplied ClassNameMatcher matches for deserialization, unless they are otherwise rejected.

    The reject list takes precedence over the accept list.

    Parameters:
      `matcher` - a class name matcher to accept objects.
    Returns:
      `this` instance.

  - ### accept

    public ValidatingObjectInputStream accept(Pattern pattern)
    Accepts class names that match the supplied pattern for deserialization, unless they are otherwise rejected.

    The reject list takes precedence over the accept list.

    Parameters:
      `pattern` - a Pattern for compiled regular expression.
    Returns:
      `this` instance.

  - ### accept

    public ValidatingObjectInputStream accept(String... patterns)
    Accepts the wildcard specified classes for deserialization, unless they are otherwise rejected.

    The reject list takes precedence over the accept list.

    Parameters:
      `patterns` - Wildcard file name patterns as defined by `FilenameUtils.wildcardMatch`.
    Returns:
      `this` instance.

  - ### invalidClassNameFound

    protected void invalidClassNameFound(String className) throws InvalidClassException
    Called to throw `InvalidClassException` if an invalid class name is found during deserialization. Can be overridden, for example to log those class names.
    Parameters:
      `className` - name of the invalid class.
    Throws:
      `InvalidClassException` - Thrown with a message containing the class name.

  - ### readObjectCast

    public <T> T readObjectCast() throws ClassNotFoundException, IOException
    Delegates to `ObjectInputStream.readObject()` and casts to the generic `T`.
    Type Parameters:
      `T` - The return type.
    Returns:
      Result from `ObjectInputStream.readObject()`.
    Throws:
      `ClassNotFoundException` - Thrown by `ObjectInputStream.readObject()`.
      `IOException` - Thrown by `ObjectInputStream.readObject()`.
      `ClassCastException` - Thrown when `ObjectInputStream.readObject()` does not match `T`.
    Since:
      2.18.0

  - ### reject

    public ValidatingObjectInputStream reject(Class<?>... classes)
    Rejects the specified classes for deserialization, even if they are otherwise accepted.

    The reject list takes precedence over the accept list.

    Parameters:
      `classes` - Classes to reject.
    Returns:
      `this` instance.

  - ### reject

    public ValidatingObjectInputStream reject(ClassNameMatcher matcher)
    Rejects class names where the supplied ClassNameMatcher matches for deserialization, even if they are otherwise accepted.

    The reject list takes precedence over the accept list.

    Parameters:
      `matcher` - a class name matcher to reject objects.
    Returns:
      `this` instance.

  - ### reject

    public ValidatingObjectInputStream reject(Pattern pattern)
    Rejects class names that match the supplied pattern for deserialization, even if they are otherwise accepted.

    The reject list takes precedence over the accept list.

    Parameters:
      `pattern` - a Pattern for compiled regular expression.
    Returns:
      `this` instance.

  - ### reject

    public ValidatingObjectInputStream reject(String... patterns)
    Rejects the wildcard specified classes for deserialization, even if they are otherwise accepted.

    The reject list takes precedence over the accept list.

    Parameters:
      `patterns` - An array of wildcard file name patterns as defined by `FilenameUtils.wildcardMatch`
    Returns:
      `this` instance.

  - ### resolveClass

    protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException
    Checks that the given object's class name conforms to requirements and if so delegates to the superclass.

    The reject list takes precedence over the accept list.

    Overrides:
      `resolveClass` in class `ObjectInputStream`
    Throws:
      `IOException`
      `ClassNotFoundException`

  - ### resolveProxyClass

    protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException
    Checks that the given names conform to requirements and if so delegates to the superclass.

    The reject list takes precedence over the accept list.

    Overrides:
      `resolveProxyClass` in class `ObjectInputStream`
    Throws:
      `IOException`
      `ClassNotFoundException`