This is an automated email from the ASF dual-hosted git repository.

oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new a7f3a91c7a8a CAMEL-23815: camel-support - Shared ObjectInputFilter 
deserialization-filter helper (#24588)
a7f3a91c7a8a is described below

commit a7f3a91c7a8a0e6a1938097ac26921cf56382bc1
Author: Andrea Cosentino <[email protected]>
AuthorDate: Sun Jul 12 16:18:35 2026 +0200

    CAMEL-23815: camel-support - Shared ObjectInputFilter 
deserialization-filter helper (#24588)
    
    * CAMEL-23815: camel-support - Shared ObjectInputFilter 
deserialization-filter helper
    
    Extract the duplicated DEFAULT_DESERIALIZATION_FILTER constant and the
    configured-pattern -> jdk.serialFilter -> Camel-default resolution into
    a new DeserializationFilterHelper in camel-support, and migrate
    camel-http-common, camel-netty, camel-netty-http, camel-vertx-http,
    camel-mina, camel-infinispan, camel-jms and camel-sjms to use it.
    
    The helper documents the two deliberate defaults in one place: the
    stream-time default with JEP-290 graph-shape limits, and the class-check
    default without them for components (jms/sjms) that can only vet the
    class after a third-party library has already deserialized the payload,
    where stream metrics can never be evaluated. No behavior change.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    
    * CAMEL-23815: Use @since 4.22 per project convention
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Fable 5 <[email protected]>
---
 .../org/apache/camel/http/common/HttpHelper.java   |  29 +---
 .../protostream/DefaultExchangeHolderUtils.java    |  24 +---
 .../DefaultExchangeHolderUtilsTest.java            |   3 +-
 .../org/apache/camel/component/jms/JmsBinding.java |  64 +--------
 .../apache/camel/component/mina/MinaConverter.java |  23 +---
 .../mina/MinaConverterDefaultFilterTest.java       |  34 ++++-
 .../component/netty/http/NettyHttpHelper.java      |  32 +----
 .../NettyHttpHelperDeserializationFilterTest.java  |   3 +-
 .../camel/component/netty/NettyConverter.java      |  23 +---
 .../netty/NettyConverterDefaultFilterTest.java     |  32 ++++-
 .../camel/component/sjms/jms/JmsBinding.java       |  61 +--------
 .../component/vertx/http/VertxHttpHelper.java      |  33 +----
 .../VertxHttpHelperDeserializationFilterTest.java  |   3 +-
 .../camel/support/DeserializationFilterHelper.java | 143 ++++++++++++++++++++
 .../example/external/NotAllowedSerializable.java   |  26 ++--
 .../support/DeserializationFilterHelperTest.java   | 147 +++++++++++++++++++++
 16 files changed, 401 insertions(+), 279 deletions(-)

diff --git 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
index 7f7a3f613e39..a95af16da5f0 100644
--- 
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
+++ 
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
@@ -36,6 +36,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.RuntimeExchangeException;
 import org.apache.camel.converter.stream.CachedOutputStream;
 import org.apache.camel.support.CamelObjectInputStream;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.support.http.HttpUtil;
 import org.apache.camel.util.CollectionHelper;
 import org.apache.camel.util.IOHelper;
@@ -46,16 +47,6 @@ import org.slf4j.LoggerFactory;
 
 public final class HttpHelper {
 
-    /**
-     * Default {@link ObjectInputFilter} pattern applied when deserializing 
Java objects with Content-Type
-     * {@code application/x-java-serialized-object}. Allows standard Java and 
Apache Camel types, denies
-     * {@code java.net.**}, and applies JEP-290 graph-shape limits ({@code 
maxdepth}, {@code maxrefs}, {@code maxbytes})
-     * as defense-in-depth. Can be overridden per-component via the {@code 
deserializationFilter} option or globally via
-     * the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
-
     private static final Logger LOG = 
LoggerFactory.getLogger(HttpHelper.class);
 
     private HttpHelper() {
@@ -138,7 +129,8 @@ public final class HttpHelper {
      * @param  context                the camel context which could help us to 
apply the customer classloader
      * @param  deserializationFilter  an {@link ObjectInputFilter} pattern 
(same syntax as {@code jdk.serialFilter}) to
      *                                apply; when {@code null} or blank the 
JVM-wide {@code jdk.serialFilter} is used if
-     *                                present, otherwise {@link 
#DEFAULT_DESERIALIZATION_FILTER} is applied
+     *                                present, otherwise
+     *                                {@link 
DeserializationFilterHelper#DEFAULT_DESERIALIZATION_FILTER} is applied
      * @return                        the java object, or <tt>null</tt> if 
input stream was <tt>null</tt>
      * @throws ClassNotFoundException is thrown if class not found
      * @throws IOException            can be thrown
@@ -151,7 +143,7 @@ public final class HttpHelper {
 
         Object answer;
         ObjectInputStream ois = new CamelObjectInputStream(is, context);
-        
ois.setObjectInputFilter(resolveDeserializationFilter(deserializationFilter));
+        
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
         try {
             answer = ois.readObject();
         } finally {
@@ -161,19 +153,6 @@ public final class HttpHelper {
         return answer;
     }
 
-    private static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
-        if (configuredPattern != null && !configuredPattern.isBlank()) {
-            return ObjectInputFilter.Config.createFilter(configuredPattern);
-        }
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            return jvmFilter;
-        }
-        LOG.debug("No JVM-wide deserialization filter (jdk.serialFilter) is 
set; applying the default Camel filter: {}",
-                DEFAULT_DESERIALIZATION_FILTER);
-        return 
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER);
-    }
-
     /**
      * Reads the request body from the given http servlet request.
      *
diff --git 
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtils.java
 
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtils.java
index 701fc4c8a2cf..c28596e7d518 100644
--- 
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtils.java
+++ 
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtils.java
@@ -19,31 +19,18 @@ package 
org.apache.camel.component.infinispan.remote.protostream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.ObjectInputFilter;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.camel.support.DefaultExchangeHolder;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.util.ClassLoadingAwareObjectInputStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Utilities for {@link DefaultExchangeHolder} and the Infinispan Protostream 
marshaller.
  */
 final class DefaultExchangeHolderUtils {
 
-    /**
-     * Default deserialization filter that restricts which classes can be 
deserialized. Allows standard Java types and
-     * Apache Camel types, denies {@code java.net.**}, and applies JEP-290 
graph-shape limits ({@code maxdepth},
-     * {@code maxrefs}, {@code maxbytes}) as defense-in-depth against 
resource-exhaustion payloads. Can be overridden
-     * via the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(DefaultExchangeHolderUtils.class);
-
     private DefaultExchangeHolderUtils() {
         // Utility class
     }
@@ -60,14 +47,7 @@ final class DefaultExchangeHolderUtils {
     static DefaultExchangeHolder deserialize(byte[] bytes) {
         try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
              ObjectInputStream ois = new 
ClassLoadingAwareObjectInputStream(bais)) {
-            ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-            if (jvmFilter != null) {
-                ois.setObjectInputFilter(jvmFilter);
-            } else {
-                LOG.debug("No JVM-wide deserialization filter set, applying 
default Camel filter: {}",
-                        DEFAULT_DESERIALIZATION_FILTER);
-                
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER));
-            }
+            
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(null));
             return (DefaultExchangeHolder) ois.readObject();
         } catch (IOException | ClassNotFoundException e) {
             throw new RuntimeException(e);
diff --git 
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtilsTest.java
 
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtilsTest.java
index c065bedcdb89..3642189c282c 100644
--- 
a/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtilsTest.java
+++ 
b/components/camel-infinispan/camel-infinispan/src/test/java/org/apache/camel/component/infinispan/remote/protostream/DefaultExchangeHolderUtilsTest.java
@@ -24,6 +24,7 @@ import com.example.external.NotAllowedSerializable;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.support.DefaultExchangeHolder;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -35,7 +36,7 @@ public class DefaultExchangeHolderUtilsTest {
 
     @Test
     public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = 
DefaultExchangeHolderUtils.DEFAULT_DESERIALIZATION_FILTER;
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
         assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
         assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
         assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
diff --git 
a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsBinding.java
 
b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsBinding.java
index e1a9849662e5..f14c46dc016e 100644
--- 
a/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsBinding.java
+++ 
b/components/camel-jms/src/main/java/org/apache/camel/component/jms/JmsBinding.java
@@ -58,6 +58,7 @@ import org.apache.camel.converter.stream.CachedOutputStream;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultExchangeHolder;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.PatternHelper;
@@ -78,15 +79,6 @@ import static 
org.apache.camel.component.jms.JmsMessageType.Text;
  * A Strategy used to convert between a Camel {@link Exchange} and {@link 
JmsMessage} to and from a JMS {@link Message}
  */
 public class JmsBinding {
-    /**
-     * Default {@link ObjectInputFilter} pattern applied as a defense-in-depth 
check on the class returned by
-     * {@link jakarta.jms.ObjectMessage#getObject()}. Allows standard Java 
types and Apache Camel types and rejects
-     * everything else. Can be overridden per-endpoint via {@link 
JmsConfiguration#setDeserializationFilter(String)} or
-     * globally via the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = "!java.net.**;java.**;javax.**;org.apache.camel.**;!*";
-
     private static final Logger LOG = 
LoggerFactory.getLogger(JmsBinding.class);
     private final JmsEndpoint endpoint;
     private final HeaderFilterStrategy headerFilterStrategy;
@@ -99,7 +91,8 @@ public class JmsBinding {
         this.headerFilterStrategy = new JmsHeaderFilterStrategy(false);
         this.jmsKeyFormatStrategy = new DefaultJmsKeyFormatStrategy();
         this.messageCreatedStrategy = null;
-        this.deserializationFilter = resolveDeserializationFilter(null);
+        this.deserializationFilter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                null, 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
     }
 
     public JmsBinding(JmsEndpoint endpoint) {
@@ -127,20 +120,8 @@ public class JmsBinding {
         if (configured == null) {
             configured = endpoint.getComponent().getDeserializationFilter();
         }
-        this.deserializationFilter = resolveDeserializationFilter(configured);
-    }
-
-    private static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
-        if (configuredPattern != null && !configuredPattern.isBlank()) {
-            return ObjectInputFilter.Config.createFilter(configuredPattern);
-        }
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            return jvmFilter;
-        }
-        LOG.debug("No JVM-wide deserialization filter set, applying default 
Camel filter: {}",
-                DEFAULT_DESERIALIZATION_FILTER);
-        return 
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER);
+        this.deserializationFilter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                configured, 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
     }
 
     /**
@@ -172,7 +153,7 @@ public class JmsBinding {
             return;
         }
         Class<?> clazz = payload.getClass();
-        ObjectInputFilter.Status status = deserializationFilter.checkInput(new 
PostDeserializationFilterInfo(clazz));
+        ObjectInputFilter.Status status = 
DeserializationFilterHelper.checkClass(deserializationFilter, clazz);
         if (status == ObjectInputFilter.Status.REJECTED) {
             throw new SecurityException(
                     "JMS ObjectMessage deserialization blocked for class: " + 
clazz.getName()
@@ -180,39 +161,6 @@ public class JmsBinding {
         }
     }
 
-    private static final class PostDeserializationFilterInfo implements 
ObjectInputFilter.FilterInfo {
-        private final Class<?> clazz;
-
-        private PostDeserializationFilterInfo(Class<?> clazz) {
-            this.clazz = clazz;
-        }
-
-        @Override
-        public Class<?> serialClass() {
-            return clazz;
-        }
-
-        @Override
-        public long arrayLength() {
-            return -1;
-        }
-
-        @Override
-        public long depth() {
-            return 0;
-        }
-
-        @Override
-        public long references() {
-            return 0;
-        }
-
-        @Override
-        public long streamBytes() {
-            return 0;
-        }
-    }
-
     /**
      * Extracts the body from the JMS message
      *
diff --git 
a/components/camel-mina/src/main/java/org/apache/camel/component/mina/MinaConverter.java
 
b/components/camel-mina/src/main/java/org/apache/camel/component/mina/MinaConverter.java
index b721ce8e1cfa..e6d175c87086 100644
--- 
a/components/camel-mina/src/main/java/org/apache/camel/component/mina/MinaConverter.java
+++ 
b/components/camel-mina/src/main/java/org/apache/camel/component/mina/MinaConverter.java
@@ -19,31 +19,19 @@ package org.apache.camel.component.mina;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInput;
-import java.io.ObjectInputFilter;
 import java.io.ObjectInputStream;
 
 import org.apache.camel.Converter;
 import org.apache.camel.Exchange;
 import org.apache.camel.StreamCache;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.mina.core.buffer.IoBuffer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * A set of converter methods for working with MINA types
  */
 @Converter(generateLoader = true)
 public final class MinaConverter {
-    private static final Logger LOG = 
LoggerFactory.getLogger(MinaConverter.class);
-
-    /**
-     * Default deserialization filter that restricts which classes can be 
deserialized. Allows standard Java types and
-     * Apache Camel types, denies {@code java.net.**}, and applies JEP-290 
graph-shape limits ({@code maxdepth},
-     * {@code maxrefs}, {@code maxbytes}) as defense-in-depth against 
resource-exhaustion payloads. Can be overridden
-     * via the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
 
     private MinaConverter() {
         //Utility Class
@@ -73,14 +61,7 @@ public final class MinaConverter {
     public static ObjectInput toObjectInput(IoBuffer buffer) throws 
IOException {
         InputStream is = toInputStream(buffer);
         ObjectInputStream ois = new ObjectInputStream(is);
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            ois.setObjectInputFilter(jvmFilter);
-        } else {
-            LOG.debug("No JVM-wide deserialization filter set, applying 
default Camel filter: {}",
-                    DEFAULT_DESERIALIZATION_FILTER);
-            
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER));
-        }
+        
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(null));
         return ois;
     }
 
diff --git 
a/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
 
b/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
index bcb2380de3f3..c7b300f9f4ad 100644
--- 
a/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
+++ 
b/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
@@ -16,17 +16,49 @@
  */
 package org.apache.camel.component.mina;
 
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInput;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+
+import org.apache.camel.support.DeserializationFilterHelper;
+import org.apache.mina.core.buffer.IoBuffer;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class MinaConverterDefaultFilterTest {
 
     @Test
     public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = MinaConverter.DEFAULT_DESERIALIZATION_FILTER;
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
         assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
         assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
         assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
     }
+
+    @Test
+    public void testToObjectInputAllowsStandardJavaType() throws Exception {
+        ObjectInput oi = MinaConverter.toObjectInput(toIoBuffer("hello"));
+        assertEquals("hello", oi.readObject());
+    }
+
+    @Test
+    public void testToObjectInputRejectsJavaNetClass() throws Exception {
+        ObjectInput oi = 
MinaConverter.toObjectInput(toIoBuffer(URI.create("http://example.com/";)));
+        assertThrows(InvalidClassException.class, oi::readObject);
+    }
+
+    private static IoBuffer toIoBuffer(Object value) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(value);
+        }
+        IoBuffer buffer = MinaConverter.toIoBuffer(baos.toByteArray());
+        buffer.flip();
+        return buffer;
+    }
 }
diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
index fbbcac910f5f..80192f05322d 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java
@@ -18,7 +18,6 @@ package org.apache.camel.component.netty.http;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.ObjectInputFilter;
 import java.io.ObjectInputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -29,32 +28,18 @@ import io.netty.handler.codec.http.HttpMethod;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.RuntimeExchangeException;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.util.CollectionHelper;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.URISupport;
 import org.apache.camel.util.UnsafeUriCharactersEncoder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Helpers.
  */
 public final class NettyHttpHelper {
 
-    /**
-     * Default {@link ObjectInputFilter} pattern applied when deserializing 
Java objects from HTTP responses with
-     * Content-Type {@code application/x-java-serialized-object}. Allows 
standard Java types and Apache Camel types,
-     * denies {@code java.net.**}, and applies JEP-290 graph-shape limits 
({@code maxdepth}, {@code maxrefs},
-     * {@code maxbytes}) as defense-in-depth against resource-exhaustion 
payloads. Can be overridden per-endpoint via
-     * {@link NettyHttpConfiguration#setDeserializationFilter(String)} or 
globally via the JVM system property
-     * {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(NettyHttpHelper.class);
-
     private NettyHttpHelper() {
     }
 
@@ -156,7 +141,7 @@ public final class NettyHttpHelper {
 
         Object answer = null;
         ObjectInputStream ois = new ObjectInputStream(is);
-        
ois.setObjectInputFilter(resolveDeserializationFilter(deserializationFilter));
+        
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
         try {
             answer = ois.readObject();
         } finally {
@@ -166,19 +151,6 @@ public final class NettyHttpHelper {
         return answer;
     }
 
-    private static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
-        if (configuredPattern != null && !configuredPattern.isBlank()) {
-            return ObjectInputFilter.Config.createFilter(configuredPattern);
-        }
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            return jvmFilter;
-        }
-        LOG.debug("No JVM-wide deserialization filter set, applying default 
Camel filter: {}",
-                DEFAULT_DESERIALIZATION_FILTER);
-        return 
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER);
-    }
-
     /**
      * Creates the URL to invoke.
      *
diff --git 
a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java
 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java
index b1dda73bdd48..f40c15d27895 100644
--- 
a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java
+++ 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java
@@ -24,6 +24,7 @@ import java.io.ObjectOutputStream;
 import java.net.URI;
 
 import com.example.external.NotAllowedSerializable;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -35,7 +36,7 @@ public class NettyHttpHelperDeserializationFilterTest {
 
     @Test
     public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = NettyHttpHelper.DEFAULT_DESERIALIZATION_FILTER;
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
         assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
         assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
         assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
diff --git 
a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
 
b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
index 9b817138efbf..36920d1034df 100644
--- 
a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
+++ 
b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
@@ -19,7 +19,6 @@ package org.apache.camel.component.netty;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInput;
-import java.io.ObjectInputFilter;
 import java.io.ObjectInputStream;
 import java.nio.charset.StandardCharsets;
 
@@ -35,24 +34,13 @@ import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.ByteBufInputStream;
 import org.apache.camel.Converter;
 import org.apache.camel.Exchange;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.camel.support.DeserializationFilterHelper;
 
 /**
  * A set of converter methods for working with Netty types
  */
 @Converter(generateLoader = true)
 public final class NettyConverter {
-    private static final Logger LOG = 
LoggerFactory.getLogger(NettyConverter.class);
-
-    /**
-     * Default deserialization filter that restricts which classes can be 
deserialized. Allows standard Java types and
-     * Apache Camel types, denies {@code java.net.**}, and applies JEP-290 
graph-shape limits ({@code maxdepth},
-     * {@code maxrefs}, {@code maxbytes}) as defense-in-depth against 
resource-exhaustion payloads. Can be overridden
-     * via the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
 
     private NettyConverter() {
         //Utility Class
@@ -93,14 +81,7 @@ public final class NettyConverter {
     public static ObjectInput toObjectInput(ByteBuf buffer, Exchange exchange) 
throws IOException {
         InputStream is = toInputStream(buffer, exchange);
         ObjectInputStream ois = new ObjectInputStream(is);
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            ois.setObjectInputFilter(jvmFilter);
-        } else {
-            LOG.debug("No JVM-wide deserialization filter set, applying 
default Camel filter: {}",
-                    DEFAULT_DESERIALIZATION_FILTER);
-            
ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER));
-        }
+        
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(null));
         return ois;
     }
 
diff --git 
a/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettyConverterDefaultFilterTest.java
 
b/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettyConverterDefaultFilterTest.java
index 455b2121c815..71a153516219 100644
--- 
a/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettyConverterDefaultFilterTest.java
+++ 
b/components/camel-netty/src/test/java/org/apache/camel/component/netty/NettyConverterDefaultFilterTest.java
@@ -16,17 +16,47 @@
  */
 package org.apache.camel.component.netty;
 
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInput;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+
+import io.netty.buffer.ByteBuf;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class NettyConverterDefaultFilterTest {
 
     @Test
     public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = NettyConverter.DEFAULT_DESERIALIZATION_FILTER;
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
         assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
         assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
         assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
     }
+
+    @Test
+    public void testToObjectInputAllowsStandardJavaType() throws Exception {
+        ObjectInput oi = NettyConverter.toObjectInput(toByteBuf("hello"), 
null);
+        assertEquals("hello", oi.readObject());
+    }
+
+    @Test
+    public void testToObjectInputRejectsJavaNetClass() throws Exception {
+        ObjectInput oi = 
NettyConverter.toObjectInput(toByteBuf(URI.create("http://example.com/";)), 
null);
+        assertThrows(InvalidClassException.class, oi::readObject);
+    }
+
+    private static ByteBuf toByteBuf(Object value) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(value);
+        }
+        return NettyConverter.toByteBuffer(baos.toByteArray());
+    }
 }
diff --git 
a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsBinding.java
 
b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsBinding.java
index 092ecc3c1e06..a66f323d5d88 100644
--- 
a/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsBinding.java
+++ 
b/components/camel-sjms/src/main/java/org/apache/camel/component/sjms/jms/JmsBinding.java
@@ -52,6 +52,7 @@ import org.apache.camel.WrappedFile;
 import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultExchangeHolder;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
@@ -65,15 +66,6 @@ import static 
org.apache.camel.component.sjms.jms.JmsMessageHelper.normalizeDest
  */
 public class JmsBinding {
 
-    /**
-     * Default {@link ObjectInputFilter} pattern applied as a defense-in-depth 
check on the class returned by
-     * {@link jakarta.jms.ObjectMessage#getObject()}. Allows standard Java 
types and Apache Camel types and rejects
-     * everything else. Can be overridden per-endpoint via {@code 
SjmsEndpoint#setDeserializationFilter(String)} or
-     * globally via the JVM system property {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = "!java.net.**;java.**;javax.**;org.apache.camel.**;!*";
-
     private static final Logger LOG = 
LoggerFactory.getLogger(JmsBinding.class);
     private final boolean mapJmsMessage;
     private final boolean allowNullBody;
@@ -109,7 +101,8 @@ public class JmsBinding {
         this.jmsJmsKeyFormatStrategy = jmsJmsKeyFormatStrategy;
         this.messageCreatedStrategy = messageCreatedStrategy;
         this.jmsMessageType = jmsMessageType;
-        this.deserializationFilter = 
resolveDeserializationFilter(deserializationFilterPattern);
+        this.deserializationFilter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                deserializationFilterPattern, 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
         this.objectMessageEnabled = objectMessageEnabled;
     }
 
@@ -128,19 +121,6 @@ public class JmsBinding {
                                          + " Set objectMessageEnabled=true on 
the SJMS endpoint or component to enable it.");
     }
 
-    private static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
-        if (configuredPattern != null && !configuredPattern.isBlank()) {
-            return ObjectInputFilter.Config.createFilter(configuredPattern);
-        }
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            return jvmFilter;
-        }
-        LOG.debug("No JVM-wide deserialization filter set, applying default 
Camel filter: {}",
-                DEFAULT_DESERIALIZATION_FILTER);
-        return 
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER);
-    }
-
     /**
      * Applies the configured (or default) deserialization filter to the class 
of an object returned by
      * {@link jakarta.jms.ObjectMessage#getObject()}. Throws {@link 
SecurityException} if the class is rejected.
@@ -156,7 +136,7 @@ public class JmsBinding {
             return;
         }
         Class<?> clazz = payload.getClass();
-        ObjectInputFilter.Status status = deserializationFilter.checkInput(new 
PostDeserializationFilterInfo(clazz));
+        ObjectInputFilter.Status status = 
DeserializationFilterHelper.checkClass(deserializationFilter, clazz);
         if (status == ObjectInputFilter.Status.REJECTED) {
             throw new SecurityException(
                     "JMS ObjectMessage deserialization blocked for class: " + 
clazz.getName()
@@ -164,39 +144,6 @@ public class JmsBinding {
         }
     }
 
-    private static final class PostDeserializationFilterInfo implements 
ObjectInputFilter.FilterInfo {
-        private final Class<?> clazz;
-
-        private PostDeserializationFilterInfo(Class<?> clazz) {
-            this.clazz = clazz;
-        }
-
-        @Override
-        public Class<?> serialClass() {
-            return clazz;
-        }
-
-        @Override
-        public long arrayLength() {
-            return -1;
-        }
-
-        @Override
-        public long depth() {
-            return 0;
-        }
-
-        @Override
-        public long references() {
-            return 0;
-        }
-
-        @Override
-        public long streamBytes() {
-            return 0;
-        }
-    }
-
     /**
      * Extracts the body from the JMS message
      *
diff --git 
a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
 
b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
index 2ac0b5c76bba..a605edbc941d 100644
--- 
a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
+++ 
b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java
@@ -28,27 +28,13 @@ import java.net.URISyntaxException;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePropertyKey;
 import org.apache.camel.Message;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.UnsafeUriCharactersEncoder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public final class VertxHttpHelper {
 
-    /**
-     * Default {@link ObjectInputFilter} pattern applied when deserializing 
Java objects from HTTP responses with
-     * Content-Type {@code application/x-java-serialized-object}. Allows 
standard Java types and Apache Camel types,
-     * denies {@code java.net.**}, and applies JEP-290 graph-shape limits 
({@code maxdepth}, {@code maxrefs},
-     * {@code maxbytes}) as defense-in-depth against resource-exhaustion 
payloads. Can be overridden per-endpoint via
-     * {@link VertxHttpConfiguration#setDeserializationFilter(String)} or 
globally via the JVM system property
-     * {@code jdk.serialFilter}.
-     */
-    static final String DEFAULT_DESERIALIZATION_FILTER
-            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(VertxHttpHelper.class);
-
     private VertxHttpHelper() {
         // Utility class
     }
@@ -145,7 +131,7 @@ public final class VertxHttpHelper {
     /**
      * Deserializes an object from the given {@link InputStream} applying an 
{@link ObjectInputFilter} resolved from the
      * supplied pattern, the JVM-wide {@code jdk.serialFilter} or the Camel 
default
-     * ({@value #DEFAULT_DESERIALIZATION_FILTER}) in that order.
+     * ({@value DeserializationFilterHelper#DEFAULT_DESERIALIZATION_FILTER}) 
in that order.
      */
     public static Object deserializeJavaObjectFromStream(InputStream is, 
String deserializationFilter)
             throws ClassNotFoundException, IOException {
@@ -155,7 +141,7 @@ public final class VertxHttpHelper {
 
         Object answer;
         ObjectInputStream ois = new ObjectInputStream(is);
-        
ois.setObjectInputFilter(resolveDeserializationFilter(deserializationFilter));
+        
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
         try {
             answer = ois.readObject();
         } finally {
@@ -165,19 +151,6 @@ public final class VertxHttpHelper {
         return answer;
     }
 
-    private static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
-        if (configuredPattern != null && !configuredPattern.isBlank()) {
-            return ObjectInputFilter.Config.createFilter(configuredPattern);
-        }
-        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
-        if (jvmFilter != null) {
-            return jvmFilter;
-        }
-        LOG.debug("No JVM-wide deserialization filter set, applying default 
Camel filter: {}",
-                DEFAULT_DESERIALIZATION_FILTER);
-        return 
ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER);
-    }
-
     /**
      * Retrieves the charset from the exchange Content-Type header, or falls 
back to the CamelCharsetName exchange
      * property when not available
diff --git 
a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java
 
b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java
index 631d091aed14..d52cb8dd4fc1 100644
--- 
a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java
+++ 
b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java
@@ -24,6 +24,7 @@ import java.io.ObjectOutputStream;
 import java.net.URI;
 
 import com.example.external.NotAllowedSerializable;
+import org.apache.camel.support.DeserializationFilterHelper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -35,7 +36,7 @@ public class VertxHttpHelperDeserializationFilterTest {
 
     @Test
     public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = VertxHttpHelper.DEFAULT_DESERIALIZATION_FILTER;
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
         assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
         assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
         assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/DeserializationFilterHelper.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/DeserializationFilterHelper.java
new file mode 100644
index 000000000000..8b1ce79e13b3
--- /dev/null
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/DeserializationFilterHelper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.camel.support;
+
+import java.io.ObjectInputFilter;
+import java.io.ObjectInputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper for resolving the JEP-290 {@link ObjectInputFilter} that components 
apply when deserializing Java objects from
+ * potentially untrusted input (message payloads, persisted exchange state, 
and so on).
+ *
+ * <p>
+ * The resolution order is the same for every component:
+ * <ol>
+ * <li>an explicitly configured filter pattern (typically a {@code 
deserializationFilter} endpoint or component
+ * option);</li>
+ * <li>the JVM-wide filter configured via the {@code jdk.serialFilter} system 
property or
+ * {@link ObjectInputFilter.Config#setSerialFilter(ObjectInputFilter)};</li>
+ * <li>a conservative Camel default.</li>
+ * </ol>
+ *
+ * @since 4.22
+ */
+public final class DeserializationFilterHelper {
+
+    /**
+     * Default deserialization filter pattern applied while reading from an 
{@link ObjectInputStream} (via
+     * {@link ObjectInputStream#setObjectInputFilter(ObjectInputFilter)}). 
Denies {@code java.net.**}, allows other
+     * standard Java types and Apache Camel types, rejects everything else, 
and applies JEP-290 graph-shape limits
+     * ({@code maxdepth}, {@code maxrefs}, {@code maxbytes}) as 
defense-in-depth against resource-exhaustion payloads.
+     */
+    public static final String DEFAULT_DESERIALIZATION_FILTER
+            = 
"!java.net.**;java.**;javax.**;org.apache.camel.**;maxdepth=20;maxrefs=10000;maxbytes=10485760;!*";
+
+    /**
+     * Default deserialization filter pattern for checking the class of an 
object that a third-party library has
+     * <em>already</em> deserialized, for example the payload returned by 
{@code jakarta.jms.ObjectMessage#getObject()}.
+     * Same allow-list as {@link #DEFAULT_DESERIALIZATION_FILTER} but 
deliberately without the JEP-290 graph-shape
+     * limits: stream metrics such as depth, reference count and byte count 
are not available once deserialization has
+     * already happened, so including the limits would only advertise 
protection that can never be enforced.
+     */
+    public static final String DEFAULT_CLASS_DESERIALIZATION_FILTER
+            = "!java.net.**;java.**;javax.**;org.apache.camel.**;!*";
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DeserializationFilterHelper.class);
+
+    private DeserializationFilterHelper() {
+    }
+
+    /**
+     * Resolves the deserialization filter to apply, falling back to {@link 
#DEFAULT_DESERIALIZATION_FILTER}.
+     *
+     * @param  configuredPattern the filter pattern configured on the 
component or endpoint, may be null or blank
+     * @return                   the filter to apply, never null
+     */
+    public static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern) {
+        return resolveDeserializationFilter(configuredPattern, 
DEFAULT_DESERIALIZATION_FILTER);
+    }
+
+    /**
+     * Resolves the deserialization filter to apply: the configured pattern 
when present, otherwise the JVM-wide filter
+     * ({@code jdk.serialFilter}) when set, otherwise the given default 
pattern.
+     *
+     * @param  configuredPattern the filter pattern configured on the 
component or endpoint, may be null or blank
+     * @param  defaultPattern    the default pattern to fall back to, must not 
be null
+     * @return                   the filter to apply, never null
+     */
+    public static ObjectInputFilter resolveDeserializationFilter(String 
configuredPattern, String defaultPattern) {
+        if (configuredPattern != null && !configuredPattern.isBlank()) {
+            return ObjectInputFilter.Config.createFilter(configuredPattern);
+        }
+        ObjectInputFilter jvmFilter = 
ObjectInputFilter.Config.getSerialFilter();
+        if (jvmFilter != null) {
+            return jvmFilter;
+        }
+        LOG.debug("No JVM-wide deserialization filter set, applying default 
Camel filter: {}", defaultPattern);
+        return ObjectInputFilter.Config.createFilter(defaultPattern);
+    }
+
+    /**
+     * Checks the class of an already-deserialized object against the given 
filter. This is for components where a
+     * third-party library performs the actual deserialization and Camel can 
only vet the result after the fact. Only
+     * the class is checked; JEP-290 graph-shape limits ({@code maxdepth} and 
friends) in the filter pattern cannot be
+     * evaluated in this mode, so resolve the filter with {@link 
#DEFAULT_CLASS_DESERIALIZATION_FILTER} as the default.
+     *
+     * @param  filter the filter to check against
+     * @param  clazz  the class of the deserialized object
+     * @return        the filter decision; callers should treat {@link 
ObjectInputFilter.Status#REJECTED} as fatal
+     */
+    public static ObjectInputFilter.Status checkClass(ObjectInputFilter 
filter, Class<?> clazz) {
+        return filter.checkInput(new ClassOnlyFilterInfo(clazz));
+    }
+
+    private static final class ClassOnlyFilterInfo implements 
ObjectInputFilter.FilterInfo {
+        private final Class<?> clazz;
+
+        private ClassOnlyFilterInfo(Class<?> clazz) {
+            this.clazz = clazz;
+        }
+
+        @Override
+        public Class<?> serialClass() {
+            return clazz;
+        }
+
+        @Override
+        public long arrayLength() {
+            return -1;
+        }
+
+        @Override
+        public long depth() {
+            return 0;
+        }
+
+        @Override
+        public long references() {
+            return 0;
+        }
+
+        @Override
+        public long streamBytes() {
+            return 0;
+        }
+    }
+}
diff --git 
a/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
 
b/core/camel-support/src/test/java/com/example/external/NotAllowedSerializable.java
similarity index 57%
copy from 
components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
copy to 
core/camel-support/src/test/java/com/example/external/NotAllowedSerializable.java
index bcb2380de3f3..ef1d0c8df170 100644
--- 
a/components/camel-mina/src/test/java/org/apache/camel/component/mina/MinaConverterDefaultFilterTest.java
+++ 
b/core/camel-support/src/test/java/com/example/external/NotAllowedSerializable.java
@@ -14,19 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.component.mina;
+package com.example.external;
 
-import org.junit.jupiter.api.Test;
+import java.io.Serializable;
 
-import static org.junit.jupiter.api.Assertions.assertTrue;
+/**
+ * Serializable type outside the {@code java.**}/{@code org.apache.camel.**} 
allow-list, used to verify that the default
+ * deserialization filters reject unlisted classes.
+ */
+public class NotAllowedSerializable implements Serializable {
+
+    private static final long serialVersionUID = 1L;
 
-public class MinaConverterDefaultFilterTest {
+    private final String value;
+
+    public NotAllowedSerializable(String value) {
+        this.value = value;
+    }
 
-    @Test
-    public void testDefaultFilterContainsGraphShapeLimits() {
-        String filter = MinaConverter.DEFAULT_DESERIALIZATION_FILTER;
-        assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
-        assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
-        assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
+    public String getValue() {
+        return value;
     }
 }
diff --git 
a/core/camel-support/src/test/java/org/apache/camel/support/DeserializationFilterHelperTest.java
 
b/core/camel-support/src/test/java/org/apache/camel/support/DeserializationFilterHelperTest.java
new file mode 100644
index 000000000000..1268bd165bdb
--- /dev/null
+++ 
b/core/camel-support/src/test/java/org/apache/camel/support/DeserializationFilterHelperTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.camel.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputFilter;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.example.external.NotAllowedSerializable;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DeserializationFilterHelperTest {
+
+    @Test
+    public void testStreamDefaultContainsGraphShapeLimits() {
+        String filter = 
DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER;
+        assertTrue(filter.contains("maxdepth="), "Expected maxdepth in filter: 
" + filter);
+        assertTrue(filter.contains("maxrefs="), "Expected maxrefs in filter: " 
+ filter);
+        assertTrue(filter.contains("maxbytes="), "Expected maxbytes in filter: 
" + filter);
+    }
+
+    @Test
+    public void testClassCheckDefaultOmitsGraphShapeLimits() {
+        String filter = 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER;
+        assertFalse(filter.contains("maxdepth="), "Graph limits cannot fire in 
class-check mode: " + filter);
+        assertFalse(filter.contains("maxrefs="), "Graph limits cannot fire in 
class-check mode: " + filter);
+        assertFalse(filter.contains("maxbytes="), "Graph limits cannot fire in 
class-check mode: " + filter);
+    }
+
+    @Test
+    public void testDefaultFilterAllowsStandardAndCamelTypes() throws 
Exception {
+        assertEquals("hello", deserialize(serialize("hello"), null));
+        assertInstanceOf(HashMap.class, deserialize(serialize(new 
HashMap<String, String>()), null));
+        assertInstanceOf(DefaultExchangeHolder.class, 
deserialize(serialize(new DefaultExchangeHolder()), null));
+    }
+
+    @Test
+    public void testDefaultFilterRejectsJavaNetClass() throws Exception {
+        byte[] data = serialize(URI.create("http://example.com/";));
+        assertThrows(InvalidClassException.class, () -> deserialize(data, 
null));
+    }
+
+    @Test
+    public void testDefaultFilterRejectsUnlistedType() throws Exception {
+        byte[] data = serialize(new NotAllowedSerializable("blocked"));
+        assertThrows(InvalidClassException.class, () -> deserialize(data, 
null));
+    }
+
+    @Test
+    public void testConfiguredFilterWinsOverDefault() throws Exception {
+        byte[] data = serialize(new NotAllowedSerializable("allowed"));
+        Object value = deserialize(data, "com.example.external.*;java.**;!*");
+        assertInstanceOf(NotAllowedSerializable.class, value);
+        assertEquals("allowed", ((NotAllowedSerializable) value).getValue());
+    }
+
+    @Test
+    public void testBlankPatternIsTreatedAsAbsent() throws Exception {
+        byte[] data = serialize(new NotAllowedSerializable("blocked"));
+        assertThrows(InvalidClassException.class, () -> deserialize(data, "  
"));
+    }
+
+    @Test
+    public void testDefaultFilterEnforcesDepthLimitOnStreams() throws 
Exception {
+        Object graph = "leaf";
+        for (int i = 0; i < 30; i++) {
+            List<Object> wrapper = new ArrayList<>();
+            wrapper.add(graph);
+            graph = wrapper;
+        }
+        byte[] data = serialize(graph);
+        assertThrows(InvalidClassException.class, () -> deserialize(data, 
null));
+    }
+
+    @Test
+    public void testCheckClassAllowsStandardAndCamelTypes() {
+        ObjectInputFilter filter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                null, 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
+        assertNotEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, HashMap.class));
+        assertNotEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, 
DefaultExchangeHolder.class));
+    }
+
+    @Test
+    public void testCheckClassRejectsJavaNetAndUnlistedTypes() {
+        ObjectInputFilter filter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                null, 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
+        assertEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, URI.class));
+        assertEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, 
NotAllowedSerializable.class));
+    }
+
+    @Test
+    public void testCheckClassHonorsConfiguredPattern() {
+        ObjectInputFilter filter = 
DeserializationFilterHelper.resolveDeserializationFilter(
+                "com.example.external.*;!*", 
DeserializationFilterHelper.DEFAULT_CLASS_DESERIALIZATION_FILTER);
+        assertNotEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, 
NotAllowedSerializable.class));
+        assertEquals(ObjectInputFilter.Status.REJECTED,
+                DeserializationFilterHelper.checkClass(filter, HashMap.class));
+    }
+
+    private static byte[] serialize(Object value) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(value);
+        }
+        return baos.toByteArray();
+    }
+
+    private static Object deserialize(byte[] data, String configuredPattern) 
throws Exception {
+        try (ObjectInputStream ois = new ObjectInputStream(new 
ByteArrayInputStream(data))) {
+            
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(configuredPattern));
+            return ois.readObject();
+        }
+    }
+}

Reply via email to