You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2026/08/24 14:56:43 UTC
(camel) branch camel-4.22.x updated: [backport camel-4.22.x] CAMEL-24421: camel-spring-redis - apply a deserialization filter to the default JDK serializer (#25588)
This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new b2ba1ed90562 [backport camel-4.22.x] CAMEL-24421: camel-spring-redis - apply a deserialization filter to the default JDK serializer (#25588)
b2ba1ed90562 is described below
commit b2ba1ed90562e10e07bfb4a3cab60b817885e6de
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Mon Aug 24 16:56:36 2026 +0200
[backport camel-4.22.x] CAMEL-24421: camel-spring-redis - apply a deserialization filter to the default JDK serializer (#25588)
CAMEL-24421: camel-spring-redis - apply a deserialization filter to the default JDK serializer
RedisConfiguration.createDefaultSerializer() built a bare
JdkSerializationRedisSerializer, whose read path runs through Spring's
ConfigurableObjectInputStream with no ObjectInputFilter installed. Every other
component in the codebase that performs JDK deserialization -- camel-consul,
camel-leveldb, camel-mina, camel-netty, camel-netty-http, camel-vertx-http,
camel-jms, camel-sjms, camel-http-common, camel-cassandraql -- resolves a filter
first, so camel-spring-redis was the one path left without one.
The default serializer now installs a filter resolved through
DeserializationFilterHelper: the new deserializationFilter option when set,
otherwise the JVM-wide jdk.serialFilter, otherwise the shared Camel allow-list.
JdkSerializationRedisSerializer exposes no hook for a filter, so the serializer
is built through its Converter-based constructor with a deserializing converter
that sets one on the stream. Serialization is left untouched, and class
resolution is unchanged: the same null class loader Spring's DefaultDeserializer
uses by default is passed through.
This covers the three places that share the default serializer -- the consumer,
which deserializes the payload of every message published to the subscribed
channels; the producer read commands, through the default RedisTemplate; and
SpringRedisIdempotentRepository, which builds that same template. Note that
setting a custom redisTemplate does not reach the consumer, which reads
getSerializer() directly, so the option covers both paths.
Setting the serializer option to a custom RedisSerializer bypasses the filter,
since Camel then no longer controls how the payload is read.
(cherry picked from commit d35b455495bc99afff0266315ff2399c7ff58b30)
Co-authored-by: Claude Opus 5 (1M context) <no...@anthropic.com>
---
.../camel/catalog/components/spring-redis.json | 3 +-
.../camel-spring-parent/camel-spring-redis/pom.xml | 5 ++
.../component/redis/RedisEndpointConfigurer.java | 6 ++
.../component/redis/RedisEndpointUriFactory.java | 3 +-
.../apache/camel/component/redis/spring-redis.json | 3 +-
.../component/redis/FilteringDeserializer.java | 55 +++++++++++++
.../camel/component/redis/RedisConfiguration.java | 27 ++++++-
...edisConfigurationDeserializationFilterTest.java | 91 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 23 ++++++
.../endpoint/dsl/RedisEndpointBuilderFactory.java | 60 ++++++++++++++
10 files changed, 272 insertions(+), 4 deletions(-)
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json
index 68a2e24cedbb..bf70f4e86a8d 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json
@@ -72,6 +72,7 @@
"exceptionHandler": { "index": 8, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By def [...]
"exchangePattern": { "index": 9, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
"listenerContainer": { "index": 10, "kind": "parameter", "displayName": "Listener Container", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.springframework.data.redis.listener.RedisMessageListenerContainer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Reference to a pre-c [...]
- "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+ "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+ "deserializationFilter": { "index": 12, "kind": "parameter", "displayName": "Deserialization Filter", "group": "security", "label": "advanced,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when the [...]
}
}
diff --git a/components/camel-spring-parent/camel-spring-redis/pom.xml b/components/camel-spring-parent/camel-spring-redis/pom.xml
index 8b6ab6b10bd8..9942878b110d 100644
--- a/components/camel-spring-parent/camel-spring-redis/pom.xml
+++ b/components/camel-spring-parent/camel-spring-redis/pom.xml
@@ -62,6 +62,11 @@
<version>${mockito-version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
diff --git a/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointConfigurer.java b/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointConfigurer.java
index 6cfe69080332..de09d38e33a4 100644
--- a/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointConfigurer.java
+++ b/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointConfigurer.java
@@ -29,6 +29,8 @@ public class RedisEndpointConfigurer extends PropertyConfigurerSupport implement
case "command": target.getConfiguration().setCommand(property(camelContext, org.apache.camel.component.redis.Command.class, value)); return true;
case "connectionfactory":
case "connectionFactory": target.getConfiguration().setConnectionFactory(property(camelContext, org.springframework.data.redis.connection.RedisConnectionFactory.class, value)); return true;
+ case "deserializationfilter":
+ case "deserializationFilter": target.getConfiguration().setDeserializationFilter(property(camelContext, java.lang.String.class, value)); return true;
case "exceptionhandler":
case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true;
case "exchangepattern":
@@ -53,6 +55,8 @@ public class RedisEndpointConfigurer extends PropertyConfigurerSupport implement
case "command": return org.apache.camel.component.redis.Command.class;
case "connectionfactory":
case "connectionFactory": return org.springframework.data.redis.connection.RedisConnectionFactory.class;
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "exceptionhandler":
case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class;
case "exchangepattern":
@@ -78,6 +82,8 @@ public class RedisEndpointConfigurer extends PropertyConfigurerSupport implement
case "command": return target.getConfiguration().getCommand();
case "connectionfactory":
case "connectionFactory": return target.getConfiguration().getConnectionFactory();
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getConfiguration().getDeserializationFilter();
case "exceptionhandler":
case "exceptionHandler": return target.getExceptionHandler();
case "exchangepattern":
diff --git a/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointUriFactory.java b/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointUriFactory.java
index 8766549b4286..fb3b42176e45 100644
--- a/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointUriFactory.java
+++ b/components/camel-spring-parent/camel-spring-redis/src/generated/java/org/apache/camel/component/redis/RedisEndpointUriFactory.java
@@ -24,11 +24,12 @@ public class RedisEndpointUriFactory extends org.apache.camel.support.component.
private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(12);
+ Set<String> props = new HashSet<>(13);
props.add("bridgeErrorHandler");
props.add("channels");
props.add("command");
props.add("connectionFactory");
+ props.add("deserializationFilter");
props.add("exceptionHandler");
props.add("exchangePattern");
props.add("host");
diff --git a/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json b/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json
index 68a2e24cedbb..bf70f4e86a8d 100644
--- a/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json
+++ b/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json
@@ -72,6 +72,7 @@
"exceptionHandler": { "index": 8, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By def [...]
"exchangePattern": { "index": 9, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
"listenerContainer": { "index": 10, "kind": "parameter", "displayName": "Listener Container", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.springframework.data.redis.listener.RedisMessageListenerContainer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Reference to a pre-c [...]
- "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+ "lazyStartProducer": { "index": 11, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+ "deserializationFilter": { "index": 12, "kind": "parameter", "displayName": "Deserialization Filter", "group": "security", "label": "advanced,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when the [...]
}
}
diff --git a/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/FilteringDeserializer.java b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/FilteringDeserializer.java
new file mode 100644
index 000000000000..d621937f42cd
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/FilteringDeserializer.java
@@ -0,0 +1,55 @@
+/*
+ * 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.component.redis;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputFilter;
+import java.io.ObjectInputStream;
+
+import org.apache.camel.support.DeserializationFilterHelper;
+import org.springframework.core.ConfigurableObjectInputStream;
+import org.springframework.core.serializer.Deserializer;
+
+/**
+ * A Spring {@link Deserializer} that installs an {@link ObjectInputFilter} on the stream before the object graph is
+ * read.
+ * <p/>
+ * Spring's own {@code DefaultDeserializer} reads through a {@link ConfigurableObjectInputStream} without any filter, so
+ * the default {@code JdkSerializationRedisSerializer} would otherwise materialise arbitrary types. This deserializer is
+ * behaviourally identical apart from the filter, and in particular resolves classes the same way by passing the same
+ * {@code null} class loader that {@code DefaultDeserializer} uses by default.
+ */
+class FilteringDeserializer implements Deserializer<Object> {
+
+ private final String deserializationFilter;
+
+ FilteringDeserializer(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
+ @Override
+ public Object deserialize(InputStream inputStream) throws IOException {
+ ObjectInputStream ois = new ConfigurableObjectInputStream(inputStream, null);
+ ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
+ try {
+ return ois.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException("Failed to deserialize object type", e);
+ }
+ }
+}
diff --git a/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisConfiguration.java b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisConfiguration.java
index cfb72db8b32d..3c58b2ac5874 100644
--- a/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisConfiguration.java
+++ b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisConfiguration.java
@@ -20,6 +20,8 @@ import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriParams;
import org.apache.camel.spi.UriPath;
+import org.springframework.core.serializer.support.DeserializingConverter;
+import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@@ -50,6 +52,13 @@ public class RedisConfiguration {
private RedisConnectionFactory connectionFactory;
@UriParam
private RedisSerializer<?> serializer;
+ @UriParam(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when the default"
+ + " JDK serializer deserializes Redis payloads, both on the consumer and on producer read"
+ + " commands. When not set, the JVM-wide jdk.serialFilter is used if present; otherwise a"
+ + " conservative default filter denying java.net.* and otherwise allowing java.*, javax.*"
+ + " and org.apache.camel.* packages is applied. Ignored when a custom serializer is set.")
+ private String deserializationFilter;
public Command getCommand() {
return command;
@@ -141,6 +150,19 @@ public class RedisConfiguration {
this.serializer = serializer;
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern ({@code jdk.serialFilter} syntax) applied when the default JDK
+ * serializer deserializes Redis payloads. When not set, the JVM-wide {@code jdk.serialFilter} is used if present,
+ * otherwise a conservative default filter is applied. Ignored when a custom serializer is set.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
private RedisConnectionFactory createDefaultConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
managedConnectionFactory = true;
@@ -174,7 +196,10 @@ public class RedisConfiguration {
}
private RedisSerializer<?> createDefaultSerializer() {
- serializer = new JdkSerializationRedisSerializer();
+ // JdkSerializationRedisSerializer exposes no hook to install an ObjectInputFilter, so supply a deserializing
+ // converter that sets one on the stream. Serialization is left untouched.
+ serializer = new JdkSerializationRedisSerializer(
+ new SerializingConverter(), new DeserializingConverter(new FilteringDeserializer(deserializationFilter)));
return serializer;
}
diff --git a/components/camel-spring-parent/camel-spring-redis/src/test/java/org/apache/camel/component/redis/RedisConfigurationDeserializationFilterTest.java b/components/camel-spring-parent/camel-spring-redis/src/test/java/org/apache/camel/component/redis/RedisConfigurationDeserializationFilterTest.java
new file mode 100644
index 000000000000..29d61fc8b4a4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-redis/src/test/java/org/apache/camel/component/redis/RedisConfigurationDeserializationFilterTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.component.redis;
+
+import java.io.InvalidClassException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * The default serializer must not materialise arbitrary types. Redis payloads reach {@code RedisConsumer.setBody()} and
+ * the producer read commands straight from the wire, so the deserialization filter is what stands between them and
+ * {@code readObject()}.
+ */
+class RedisConfigurationDeserializationFilterTest {
+
+ @SuppressWarnings("unchecked")
+ private static RedisSerializer<Object> serializerOf(RedisConfiguration configuration) {
+ return (RedisSerializer<Object>) configuration.getSerializer();
+ }
+
+ @Test
+ void allowedTypeRoundTrips() {
+ RedisSerializer<Object> serializer = serializerOf(new RedisConfiguration());
+ List<String> body = new ArrayList<>(List.of("a", "b"));
+
+ assertThat(serializer.deserialize(serializer.serialize(body))).isEqualTo(body);
+ }
+
+ @Test
+ void deniedJavaNetTypeIsRejected() {
+ RedisSerializer<Object> serializer = serializerOf(new RedisConfiguration());
+ byte[] payload = serializer.serialize(URI.create("http://localhost/"));
+
+ assertThatThrownBy(() -> serializer.deserialize(payload))
+ .isInstanceOf(SerializationException.class)
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+ }
+
+ @Test
+ void typeOutsideTheAllowListIsRejected() {
+ RedisSerializer<Object> serializer = serializerOf(new RedisConfiguration());
+ // a Serializable type that is not java.*, javax.* or org.apache.camel.*, so the trailing !* denies it
+ byte[] payload = serializer.serialize(new SerializationException("outside the default allow-list"));
+
+ assertThatThrownBy(() -> serializer.deserialize(payload))
+ .isInstanceOf(SerializationException.class)
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+ }
+
+ @Test
+ void configuredFilterOverridesTheDefault() {
+ RedisConfiguration configuration = new RedisConfiguration();
+ configuration.setDeserializationFilter("org.springframework.**;java.**;!*");
+ RedisSerializer<Object> serializer = serializerOf(configuration);
+ SerializationException outsideDefaultAllowList = new SerializationException("allowed by the custom filter");
+
+ assertThat(serializer.deserialize(serializer.serialize(outsideDefaultAllowList)))
+ .isInstanceOf(SerializationException.class);
+ }
+
+ @Test
+ void customSerializerIsLeftAlone() {
+ RedisConfiguration configuration = new RedisConfiguration();
+ RedisSerializer<String> custom = RedisSerializer.string();
+ configuration.setSerializer(custom);
+
+ assertThat(configuration.getSerializer()).isSameAs(custom);
+ }
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 1c4c03497e19..ee441edd9316 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -1744,3 +1744,26 @@ they say:
`CamelGoogleVertexAIStreamOutputMode` header.
* `jsonMode=true` sets the response MIME type of the request to `application/json`, so the model is
asked to answer with JSON. The default `false` leaves the request untouched.
+
+=== camel-spring-redis - the default serializer applies a deserialization filter
+
+The default serializer, `JdkSerializationRedisSerializer`, now installs a JEP-290
+`java.io.ObjectInputFilter` while reading Redis payloads, resolved through
+`DeserializationFilterHelper`. Previously no filter was applied at all. This affects both the
+consumer, which deserializes the payload of every message published to the subscribed channels,
+and the producer read commands, which deserialize the values stored in Redis.
+
+When no explicit pattern is configured, the JVM-wide `jdk.serialFilter` is honoured if set,
+otherwise the shared Camel default allow-list is applied (it permits standard Java and Apache Camel
+types, denies `java.net.**`, and enforces JEP-290 graph-shape limits). Routes that exchange classes
+outside that allow-list must widen it through the new `deserializationFilter` endpoint option, for
+example:
+
+[source,java]
+----
+from("spring-redis://localhost:6379?command=SUBSCRIBE&channels=myChannel"
+ + "&deserializationFilter=com.example.model.**;java.**;!*")
+----
+
+Setting the `serializer` option to a custom `RedisSerializer` bypasses the filter entirely, since
+Camel then no longer controls how the payload is read.
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/RedisEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/RedisEndpointBuilderFactory.java
index 8799a97fbdb4..02f5bbb411e5 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/RedisEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/RedisEndpointBuilderFactory.java
@@ -344,6 +344,26 @@ public interface RedisEndpointBuilderFactory {
doSetProperty("listenerContainer", listenerContainer);
return this;
}
+ /**
+ * Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied
+ * when the default JDK serializer deserializes Redis payloads, both on
+ * the consumer and on producer read commands. When not set, the
+ * JVM-wide jdk.serialFilter is used if present; otherwise a
+ * conservative default filter denying java.net. and otherwise allowing
+ * java., javax. and org.apache.camel. packages is applied. Ignored when
+ * a custom serializer is set.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param deserializationFilter the value to set
+ * @return the dsl builder
+ */
+ default AdvancedRedisEndpointConsumerBuilder deserializationFilter(String deserializationFilter) {
+ doSetProperty("deserializationFilter", deserializationFilter);
+ return this;
+ }
}
/**
@@ -553,6 +573,26 @@ public interface RedisEndpointBuilderFactory {
doSetProperty("lazyStartProducer", lazyStartProducer);
return this;
}
+ /**
+ * Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied
+ * when the default JDK serializer deserializes Redis payloads, both on
+ * the consumer and on producer read commands. When not set, the
+ * JVM-wide jdk.serialFilter is used if present; otherwise a
+ * conservative default filter denying java.net. and otherwise allowing
+ * java., javax. and org.apache.camel. packages is applied. Ignored when
+ * a custom serializer is set.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param deserializationFilter the value to set
+ * @return the dsl builder
+ */
+ default AdvancedRedisEndpointProducerBuilder deserializationFilter(String deserializationFilter) {
+ doSetProperty("deserializationFilter", deserializationFilter);
+ return this;
+ }
}
/**
@@ -720,6 +760,26 @@ public interface RedisEndpointBuilderFactory {
return (RedisEndpointBuilder) this;
}
+ /**
+ * Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied
+ * when the default JDK serializer deserializes Redis payloads, both on
+ * the consumer and on producer read commands. When not set, the
+ * JVM-wide jdk.serialFilter is used if present; otherwise a
+ * conservative default filter denying java.net. and otherwise allowing
+ * java., javax. and org.apache.camel. packages is applied. Ignored when
+ * a custom serializer is set.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param deserializationFilter the value to set
+ * @return the dsl builder
+ */
+ default AdvancedRedisEndpointBuilder deserializationFilter(String deserializationFilter) {
+ doSetProperty("deserializationFilter", deserializationFilter);
+ return this;
+ }
}
public interface RedisBuilders {