Skip to content

Add an optional JEP-290 ObjectInputFilter to JdkSerializer - #12862

Open
Nexory wants to merge 2 commits into
micronaut-projects:5.2.xfrom
Nexory:harden/jdkserializer-objectinputfilter
Open

Add an optional JEP-290 ObjectInputFilter to JdkSerializer#12862
Nexory wants to merge 2 commits into
micronaut-projects:5.2.xfrom
Nexory:harden/jdkserializer-objectinputfilter

Conversation

@Nexory

@Nexory Nexory commented Aug 3, 2026

Copy link
Copy Markdown

Closes #12861

What / Why

JdkSerializer is Micronaut's default ObjectSerializer (ObjectSerializer.JDK) and the default value serializer used by micronaut-redis for cache and HTTP session storage, so its deserialize(...) path reads Java-serialized bytes back from a process-external store (Redis, and other cache/session backends). Today createObjectInput(...) overrides only resolveClass(...) for classloader resolution and installs no java.io.ObjectInputFilter (JEP-290), so any class on the classpath is deserialized from that untrusted input. Installing a JEP-290 ObjectInputFilter on this path is the standard mitigation for this class of deserialization exposure.

This PR adds an optional, opt-in ObjectInputFilter to JdkSerializer:

  • A new constructor JdkSerializer(ConversionService, ObjectInputFilter) to install a scoped filter programmatically.
  • An optional system property micronaut.serializer.jdk.serial-filter, taking a standard ObjectInputFilter.Config.createFilter(String) pattern, so a deployment can enable filtering on this path without subclassing ObjectInputStream.
  • The filter is applied to the stream in createObjectInput(...). resolveClass(...) is left unchanged.

Non-breaking

The default is unchanged. With no property set and no filter argument the filter is null and behaviour is byte-for-byte identical to before. Existing callers, including the shared ObjectSerializer.JDK instance, are unaffected.

Tests

JdkSerializerSpec adds two cases: a restrictive filter makes deserialization of a disallowed class fail (surfaced as SerializationException), and a filter that allows the required type still round-trips. The existing round-trip and null cases are unchanged.

Notes

Kept intentionally minimal and opt-in so it cannot affect existing deployments. Happy to adjust the surface to whatever the team prefers: default the filter on with a conservative resource-limit pattern (maxdepth / maxrefs / maxbytes / maxarray), bind it through @ConfigurationProperties instead of a system property, or keep only the constructor and drop the property. Whichever default is chosen, the single choke point stays in one place.

JdkSerializer.deserialize reads Java-serialized bytes back from a
process-external store (it is the default cache and session value
serializer used by micronaut-redis) and previously installed no
java.io.ObjectInputFilter, so any class on the classpath could be
deserialized from that input. Add an opt-in filter, configurable via a
new JdkSerializer(ConversionService, ObjectInputFilter) constructor and
an optional micronaut.serializer.jdk.serial-filter system property, and
apply it in createObjectInput. The default is unchanged (no filter) so
existing deployments are unaffected. resolveClass is left untouched.
@cla-assistant

cla-assistant Bot commented Aug 3, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an opt-in JEP-290 ObjectInputFilter integration to Micronaut’s JdkSerializer deserialization path, allowing deployments (e.g., Redis-backed cache/session storage) to restrict which classes may be deserialized from untrusted external stores.

Changes:

  • Added an optional ObjectInputFilter to JdkSerializer, including a system-property-based default (micronaut.serializer.jdk.serial-filter).
  • Applied the filter to ObjectInputStream instances created during deserialize(...).
  • Added Spock tests covering both rejection and acceptance cases when using a filter.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
core/src/main/java/io/micronaut/core/serialize/JdkSerializer.java Adds optional ObjectInputFilter support (constructor + system property) and applies the filter during object input stream creation.
core/src/test/groovy/io/micronaut/core/serialize/JdkSerializerSpec.groovy Adds tests validating that disallowed classes are rejected and allowed classes still round-trip under the filter.

Comment thread core/src/main/java/io/micronaut/core/serialize/JdkSerializer.java
Comment thread core/src/main/java/io/micronaut/core/serialize/JdkSerializer.java Outdated
- Wrap the IllegalArgumentException from ObjectInputFilter.Config.createFilter
  so a misconfigured micronaut.serializer.jdk.serial-filter names the property
  and the offending value.
- Fix the createObjectInput javadoc return type (ObjectInputStream).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optionally apply a JEP-290 ObjectInputFilter in JdkSerializer

2 participants