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();
+ }
+ }
+}