This is an automated email from the ASF dual-hosted git repository.
davsclaus 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 998a60666f72 CAMEL-24725: apply the shared deserialization filter in
KeyValueRepositoryHelper
998a60666f72 is described below
commit 998a60666f72e53d62b51c5cab249f3be00ed860
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Sep 15 06:51:46 2026 +0200
CAMEL-24725: apply the shared deserialization filter in
KeyValueRepositoryHelper
KeyValueRepositoryHelper (new in 4.23, CAMEL-24463) deserialized stored
values with a
plain ObjectInputStream and no JEP-290 ObjectInputFilter, unlike every other
ObjectInputStream call site in Camel which goes through
DeserializationFilterHelper.
All deserialize(...) overloads now resolve a filter through
DeserializationFilterHelper (explicit pattern, then jdk.serialFilter, then
the
conservative Camel default), and filter-accepting overloads let callers
pass a
configured pattern. The persistent backends (cassandraql, hazelcast,
infinispan, jpa,
kafka, redis, sql) expose a `deserializationFilter` option so applications
storing
their own Serializable classes can widen the allow-list. In-memory backends
are
unaffected. The key-value-repository docs gain a Serialization section.
main only: the helper has not shipped in a release.
Closes #26443
Co-Authored-By: Claude Opus 5 <[email protected]>
---
.../catalog/beans/CassandraKeyValueRepository.json | 2 +-
.../catalog/beans/HazelcastKeyValueRepository.json | 2 +-
.../beans/InfinispanRemoteKeyValueRepository.json | 2 +-
.../catalog/beans/JdbcKeyValueRepository.json | 2 +-
.../camel/catalog/beans/JpaKeyValueRepository.json | 2 +-
.../catalog/beans/KafkaKeyValueRepository.json | 2 +-
.../catalog/beans/RedisKeyValueRepository.json | 2 +-
.../CassandraKeyValueRepositoryConfigurer.java | 6 ++
.../camel/bean/CassandraKeyValueRepository.json | 2 +-
.../cassandra/CassandraKeyValueRepository.java | 25 ++++++-
.../HazelcastKeyValueRepositoryConfigurer.java | 6 ++
.../camel/bean/HazelcastKeyValueRepository.json | 2 +-
.../hazelcast/HazelcastKeyValueRepository.java | 29 +++++++-
...finispanRemoteKeyValueRepositoryConfigurer.java | 6 ++
.../bean/InfinispanRemoteKeyValueRepository.json | 2 +-
.../remote/InfinispanRemoteKeyValueRepository.java | 33 +++++++--
.../jpa/JpaKeyValueRepositoryConfigurer.java | 6 ++
.../apache/camel/bean/JpaKeyValueRepository.json | 2 +-
.../keyvalue/jpa/JpaKeyValueRepository.java | 32 ++++++--
.../kafka/KafkaKeyValueRepositoryConfigurer.java | 6 ++
.../apache/camel/bean/KafkaKeyValueRepository.json | 2 +-
.../keyvalue/kafka/KafkaKeyValueRepository.java | 23 +++++-
.../redis/RedisKeyValueRepositoryConfigurer.java | 6 ++
.../apache/camel/bean/RedisKeyValueRepository.json | 2 +-
.../component/redis/RedisKeyValueRepository.java | 29 +++++++-
.../jdbc/JdbcKeyValueRepositoryConfigurer.java | 6 ++
.../apache/camel/bean/JdbcKeyValueRepository.json | 2 +-
.../keyvalue/jdbc/JdbcKeyValueRepository.java | 23 +++++-
.../camel/support/KeyValueRepositoryHelper.java | 85 ++++++++++++++++++----
.../support/KeyValueRepositoryHelperTest.java | 74 +++++++++++++++++++
.../modules/ROOT/pages/key-value-repository.adoc | 35 +++++++++
31 files changed, 406 insertions(+), 52 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/CassandraKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/CassandraKeyValueRepository.json
index 56c9f95199d2..67355e21c3d3 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/CassandraKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/CassandraKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-cassandraql",
"version": "4.23.0-SNAPSHOT",
- "properties": { "session": { "index": 0, "kind": "property",
"displayName": "Session", "required": true, "type": "object", "javaType":
"org.apache.camel.utils.cassandra.CassandraSessionHolder", "deprecated": false,
"autowired": false, "secret": false, "description": "Cassandra session" },
"table": { "index": 1, "kind": "property", "displayName": "Table", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "def [...]
+ "properties": { "session": { "index": 0, "kind": "property",
"displayName": "Session", "required": true, "type": "object", "javaType":
"org.apache.camel.utils.cassandra.CassandraSessionHolder", "deprecated": false,
"autowired": false, "secret": false, "description": "Cassandra session" },
"table": { "index": 1, "kind": "property", "displayName": "Table", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "def [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/HazelcastKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/HazelcastKeyValueRepository.json
index 22d363a3f59c..6edaa0c3c058 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/HazelcastKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/HazelcastKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-hazelcast",
"version": "4.23.0-SNAPSHOT",
- "properties": { "mapName": { "index": 0, "kind": "property",
"displayName": "Map Name", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "HazelcastKeyValueRepository", "description": "Name of the
Hazelcast map to use" }, "hazelcastInstance": { "index": 1, "kind": "property",
"displayName": "Hazelcast Instance", "required": false, "type": "object",
"javaType": "com.hazelcast.core.HazelcastIns [...]
+ "properties": { "mapName": { "index": 0, "kind": "property",
"displayName": "Map Name", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "HazelcastKeyValueRepository", "description": "Name of the
Hazelcast map to use" }, "hazelcastInstance": { "index": 1, "kind": "property",
"displayName": "Hazelcast Instance", "required": false, "type": "object",
"javaType": "com.hazelcast.core.HazelcastIns [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/InfinispanRemoteKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/InfinispanRemoteKeyValueRepository.json
index 7a920e4f70b2..bce4c304e5c1 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/InfinispanRemoteKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/InfinispanRemoteKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-infinispan",
"version": "4.23.0-SNAPSHOT",
- "properties": { "cacheName": { "index": 0, "kind": "property",
"displayName": "Cache Name", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Name of cache" }, "configuration": { "index": 1, "kind":
"property", "displayName": "Configuration", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration",
"deprecated": false, "auto [...]
+ "properties": { "cacheName": { "index": 0, "kind": "property",
"displayName": "Cache Name", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Name of cache" }, "configuration": { "index": 1, "kind":
"property", "displayName": "Configuration", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration",
"deprecated": false, "auto [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JdbcKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JdbcKeyValueRepository.json
index 07bc8667d665..2042d4a6633e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JdbcKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JdbcKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-sql",
"version": "4.23.0-SNAPSHOT",
- "properties": { "jdbcTemplate": { "index": 0, "kind": "property",
"displayName": "Jdbc Template", "required": true, "type": "object", "javaType":
"org.springframework.jdbc.core.JdbcTemplate", "deprecated": false, "autowired":
false, "secret": false, "description": "The Spring JdbcTemplate to use for
connecting to the database" }, "transactionTemplate": { "index": 1, "kind":
"property", "displayName": "Transaction Template", "required": true, "type":
"object", "javaType": "org.springf [...]
+ "properties": { "jdbcTemplate": { "index": 0, "kind": "property",
"displayName": "Jdbc Template", "required": true, "type": "object", "javaType":
"org.springframework.jdbc.core.JdbcTemplate", "deprecated": false, "autowired":
false, "secret": false, "description": "The Spring JdbcTemplate to use for
connecting to the database" }, "transactionTemplate": { "index": 1, "kind":
"property", "displayName": "Transaction Template", "required": true, "type":
"object", "javaType": "org.springf [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JpaKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JpaKeyValueRepository.json
index 2ec1140fe36b..6073d5e78960 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JpaKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/JpaKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-jpa",
"version": "4.23.0-SNAPSHOT",
- "properties": { "entityManagerFactory": { "index": 0, "kind": "property",
"displayName": "Entity Manager Factory", "required": true, "type": "object",
"javaType": "jakarta.persistence.EntityManagerFactory", "deprecated": false,
"autowired": false, "secret": false, "description": "The JPA
EntityManagerFactory to use" }, "transactionStrategy": { "index": 1, "kind":
"property", "displayName": "Transaction Strategy", "required": false, "type":
"object", "javaType": "org.apache.camel.comp [...]
+ "properties": { "entityManagerFactory": { "index": 0, "kind": "property",
"displayName": "Entity Manager Factory", "required": true, "type": "object",
"javaType": "jakarta.persistence.EntityManagerFactory", "deprecated": false,
"autowired": false, "secret": false, "description": "The JPA
EntityManagerFactory to use" }, "transactionStrategy": { "index": 1, "kind":
"property", "displayName": "Transaction Strategy", "required": false, "type":
"object", "javaType": "org.apache.camel.comp [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/KafkaKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/KafkaKeyValueRepository.json
index dd228413c88e..d7c3d1900b55 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/KafkaKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/KafkaKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-kafka",
"version": "4.23.0-SNAPSHOT",
- "properties": { "consumerConfig": { "index": 0, "kind": "property",
"displayName": "Consumer Config", "required": false, "type": "object",
"javaType": "java.util.Properties", "deprecated": false, "autowired": false,
"secret": false, "description": "Custom properties for the Kafka consumer" },
"producerConfig": { "index": 1, "kind": "property", "displayName": "Producer
Config", "required": false, "type": "object", "javaType":
"java.util.Properties", "deprecated": false, "autowired": f [...]
+ "properties": { "consumerConfig": { "index": 0, "kind": "property",
"displayName": "Consumer Config", "required": false, "type": "object",
"javaType": "java.util.Properties", "deprecated": false, "autowired": false,
"secret": false, "description": "Custom properties for the Kafka consumer" },
"producerConfig": { "index": 1, "kind": "property", "displayName": "Producer
Config", "required": false, "type": "object", "javaType":
"java.util.Properties", "deprecated": false, "autowired": f [...]
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/RedisKeyValueRepository.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/RedisKeyValueRepository.json
index 35fad0e6c77f..c8fc56830883 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/RedisKeyValueRepository.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/beans/RedisKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-redis",
"version": "4.23.0-SNAPSHOT",
- "properties": { "redisson": { "index": 0, "kind": "property",
"displayName": "Redisson", "label": "advanced", "required": false, "type":
"object", "javaType": "org.redisson.api.RedissonClient", "deprecated": false,
"autowired": false, "secret": false, "description": "To use an existing
Redisson client to connect to Redis server" }, "endpoint": { "index": 1,
"kind": "property", "displayName": "Endpoint", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": [...]
+ "properties": { "redisson": { "index": 0, "kind": "property",
"displayName": "Redisson", "label": "advanced", "required": false, "type":
"object", "javaType": "org.redisson.api.RedissonClient", "deprecated": false,
"autowired": false, "secret": false, "description": "To use an existing
Redisson client to connect to Redis server" }, "endpoint": { "index": 1,
"kind": "property", "displayName": "Endpoint", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": [...]
}
}
diff --git
a/components/camel-cassandraql/src/generated/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepositoryConfigurer.java
b/components/camel-cassandraql/src/generated/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepositoryConfigurer.java
index 88dceac9d741..761ea0924963 100644
---
a/components/camel-cassandraql/src/generated/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepositoryConfigurer.java
+++
b/components/camel-cassandraql/src/generated/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepositoryConfigurer.java
@@ -23,6 +23,8 @@ public class CassandraKeyValueRepositoryConfigurer extends
org.apache.camel.supp
public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
org.apache.camel.processor.keyvalue.cassandra.CassandraKeyValueRepository
target =
(org.apache.camel.processor.keyvalue.cassandra.CassandraKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "readconsistencylevel":
case "readConsistencyLevel":
target.setReadConsistencyLevel(property(camelContext,
com.datastax.oss.driver.api.core.ConsistencyLevel.class, value)); return true;
case "session": target.setSession(property(camelContext,
com.datastax.oss.driver.api.core.CqlSession.class, value)); return true;
@@ -36,6 +38,8 @@ public class CassandraKeyValueRepositoryConfigurer extends
org.apache.camel.supp
@Override
public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "readconsistencylevel":
case "readConsistencyLevel": return
com.datastax.oss.driver.api.core.ConsistencyLevel.class;
case "session": return
com.datastax.oss.driver.api.core.CqlSession.class;
@@ -50,6 +54,8 @@ public class CassandraKeyValueRepositoryConfigurer extends
org.apache.camel.supp
public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
org.apache.camel.processor.keyvalue.cassandra.CassandraKeyValueRepository
target =
(org.apache.camel.processor.keyvalue.cassandra.CassandraKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "readconsistencylevel":
case "readConsistencyLevel": return target.getReadConsistencyLevel();
case "session": return target.getSession();
diff --git
a/components/camel-cassandraql/src/generated/resources/META-INF/services/org/apache/camel/bean/CassandraKeyValueRepository.json
b/components/camel-cassandraql/src/generated/resources/META-INF/services/org/apache/camel/bean/CassandraKeyValueRepository.json
index 56c9f95199d2..67355e21c3d3 100644
---
a/components/camel-cassandraql/src/generated/resources/META-INF/services/org/apache/camel/bean/CassandraKeyValueRepository.json
+++
b/components/camel-cassandraql/src/generated/resources/META-INF/services/org/apache/camel/bean/CassandraKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-cassandraql",
"version": "4.23.0-SNAPSHOT",
- "properties": { "session": { "index": 0, "kind": "property",
"displayName": "Session", "required": true, "type": "object", "javaType":
"org.apache.camel.utils.cassandra.CassandraSessionHolder", "deprecated": false,
"autowired": false, "secret": false, "description": "Cassandra session" },
"table": { "index": 1, "kind": "property", "displayName": "Table", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "def [...]
+ "properties": { "session": { "index": 0, "kind": "property",
"displayName": "Session", "required": true, "type": "object", "javaType":
"org.apache.camel.utils.cassandra.CassandraSessionHolder", "deprecated": false,
"autowired": false, "secret": false, "description": "Cassandra session" },
"table": { "index": 1, "kind": "property", "displayName": "Table", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "def [...]
}
}
diff --git
a/components/camel-cassandraql/src/main/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepository.java
b/components/camel-cassandraql/src/main/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepository.java
index 74136447a9d4..0e4089591ba7 100644
---
a/components/camel-cassandraql/src/main/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepository.java
+++
b/components/camel-cassandraql/src/main/java/org/apache/camel/processor/keyvalue/cassandra/CassandraKeyValueRepository.java
@@ -95,6 +95,13 @@ public class CassandraKeyValueRepository extends
ServiceSupport implements KeyVa
@Metadata(description = "Read consistency level",
enums =
"ANY,ONE,TWO,THREE,QUORUM,ALL,LOCAL_ONE,LOCAL_QUORUM,EACH_QUORUM,SERIAL,LOCAL_SERIAL")
private ConsistencyLevel readConsistencyLevel;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
private PreparedStatement insertStatement;
private PreparedStatement insertWithTtlStatement;
@@ -280,7 +287,7 @@ public class CassandraKeyValueRepository extends
ServiceSupport implements KeyVa
return null;
}
ByteBuffer buffer = row.getByteBuffer(VALUE_COLUMN);
- return buffer != null ? KeyValueRepositoryHelper.deserialize(buffer) :
null;
+ return buffer != null ? KeyValueRepositoryHelper.deserialize(buffer,
deserializationFilter) : null;
}
/**
@@ -368,7 +375,7 @@ public class CassandraKeyValueRepository extends
ServiceSupport implements KeyVa
}
// Insert was not applied; return the existing value from the result
row
ByteBuffer existingBuffer = row.getByteBuffer(VALUE_COLUMN);
- return existingBuffer != null ?
KeyValueRepositoryHelper.deserialize(existingBuffer) : null;
+ return existingBuffer != null ?
KeyValueRepositoryHelper.deserialize(existingBuffer, deserializationFilter) :
null;
}
/**
@@ -476,4 +483,18 @@ public class CassandraKeyValueRepository extends
ServiceSupport implements KeyVa
public void setReadConsistencyLevel(ConsistencyLevel readConsistencyLevel)
{
this.readConsistencyLevel = readConsistencyLevel;
}
+
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
}
diff --git
a/components/camel-hazelcast/src/generated/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepositoryConfigurer.java
b/components/camel-hazelcast/src/generated/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepositoryConfigurer.java
index d2b8190ec5d7..f0ad0320361f 100644
---
a/components/camel-hazelcast/src/generated/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepositoryConfigurer.java
+++
b/components/camel-hazelcast/src/generated/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepositoryConfigurer.java
@@ -23,6 +23,8 @@ public class HazelcastKeyValueRepositoryConfigurer extends
org.apache.camel.supp
public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
org.apache.camel.component.hazelcast.HazelcastKeyValueRepository
target = (org.apache.camel.component.hazelcast.HazelcastKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "hazelcastinstance":
case "hazelcastInstance":
target.setHazelcastInstance(property(camelContext,
com.hazelcast.core.HazelcastInstance.class, value)); return true;
case "mapname":
@@ -34,6 +36,8 @@ public class HazelcastKeyValueRepositoryConfigurer extends
org.apache.camel.supp
@Override
public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "hazelcastinstance":
case "hazelcastInstance": return
com.hazelcast.core.HazelcastInstance.class;
case "mapname":
@@ -46,6 +50,8 @@ public class HazelcastKeyValueRepositoryConfigurer extends
org.apache.camel.supp
public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
org.apache.camel.component.hazelcast.HazelcastKeyValueRepository
target = (org.apache.camel.component.hazelcast.HazelcastKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "hazelcastinstance":
case "hazelcastInstance": return target.getHazelcastInstance();
case "mapname":
diff --git
a/components/camel-hazelcast/src/generated/resources/META-INF/services/org/apache/camel/bean/HazelcastKeyValueRepository.json
b/components/camel-hazelcast/src/generated/resources/META-INF/services/org/apache/camel/bean/HazelcastKeyValueRepository.json
index 22d363a3f59c..6edaa0c3c058 100644
---
a/components/camel-hazelcast/src/generated/resources/META-INF/services/org/apache/camel/bean/HazelcastKeyValueRepository.json
+++
b/components/camel-hazelcast/src/generated/resources/META-INF/services/org/apache/camel/bean/HazelcastKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-hazelcast",
"version": "4.23.0-SNAPSHOT",
- "properties": { "mapName": { "index": 0, "kind": "property",
"displayName": "Map Name", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "HazelcastKeyValueRepository", "description": "Name of the
Hazelcast map to use" }, "hazelcastInstance": { "index": 1, "kind": "property",
"displayName": "Hazelcast Instance", "required": false, "type": "object",
"javaType": "com.hazelcast.core.HazelcastIns [...]
+ "properties": { "mapName": { "index": 0, "kind": "property",
"displayName": "Map Name", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "HazelcastKeyValueRepository", "description": "Name of the
Hazelcast map to use" }, "hazelcastInstance": { "index": 1, "kind": "property",
"displayName": "Hazelcast Instance", "required": false, "type": "object",
"javaType": "com.hazelcast.core.HazelcastIns [...]
}
}
diff --git
a/components/camel-hazelcast/src/main/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepository.java
b/components/camel-hazelcast/src/main/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepository.java
index fd8df970d4da..cc4de61d08ba 100644
---
a/components/camel-hazelcast/src/main/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepository.java
+++
b/components/camel-hazelcast/src/main/java/org/apache/camel/component/hazelcast/HazelcastKeyValueRepository.java
@@ -70,6 +70,13 @@ public class HazelcastKeyValueRepository extends
ServiceSupport implements KeyVa
@Metadata(description = "To use an existing Hazelcast instance instead of
creating a local one")
private HazelcastInstance hazelcastInstance;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
/**
* Creates a new Hazelcast-backed key-value repository with default
settings (local instance, default map name).
@@ -109,11 +116,25 @@ public class HazelcastKeyValueRepository extends
ServiceSupport implements KeyVa
this.hazelcastInstance = hazelcastInstance;
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
@Override
@ManagedOperation(description = "Get value by key")
public Object get(String key) {
byte[] bytes = map.get(key);
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -126,14 +147,14 @@ public class HazelcastKeyValueRepository extends
ServiceSupport implements KeyVa
} else {
previous = map.put(key, serialized);
}
- return previous != null ?
KeyValueRepositoryHelper.deserialize(previous) : null;
+ return previous != null ?
KeyValueRepositoryHelper.deserialize(previous, deserializationFilter) : null;
}
@Override
@ManagedOperation(description = "Delete a key")
public Object delete(String key) {
byte[] bytes = map.remove(key);
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -162,7 +183,7 @@ public class HazelcastKeyValueRepository extends
ServiceSupport implements KeyVa
} else {
existing = map.putIfAbsent(key, serialized);
}
- return existing != null ?
KeyValueRepositoryHelper.deserialize(existing) : null;
+ return existing != null ?
KeyValueRepositoryHelper.deserialize(existing, deserializationFilter) : null;
}
@Override
diff --git
a/components/camel-infinispan/camel-infinispan/src/generated/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryConfigurer.java
b/components/camel-infinispan/camel-infinispan/src/generated/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryConfigurer.java
index 74a1b158fff4..f1571e15ae48 100644
---
a/components/camel-infinispan/camel-infinispan/src/generated/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryConfigurer.java
+++
b/components/camel-infinispan/camel-infinispan/src/generated/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepositoryConfigurer.java
@@ -28,6 +28,8 @@ public class InfinispanRemoteKeyValueRepositoryConfigurer
extends org.apache.cam
case "cachename":
case "cacheName": target.setCacheName(property(camelContext,
java.lang.String.class, value)); return true;
case "configuration": target.setConfiguration(property(camelContext,
org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration.class,
value)); return true;
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
default: return false;
}
}
@@ -40,6 +42,8 @@ public class InfinispanRemoteKeyValueRepositoryConfigurer
extends org.apache.cam
case "cachename":
case "cacheName": return java.lang.String.class;
case "configuration": return
org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration.class;
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
default: return null;
}
}
@@ -53,6 +57,8 @@ public class InfinispanRemoteKeyValueRepositoryConfigurer
extends org.apache.cam
case "cachename":
case "cacheName": return target.getCacheName();
case "configuration": return target.getConfiguration();
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
default: return null;
}
}
diff --git
a/components/camel-infinispan/camel-infinispan/src/generated/resources/META-INF/services/org/apache/camel/bean/InfinispanRemoteKeyValueRepository.json
b/components/camel-infinispan/camel-infinispan/src/generated/resources/META-INF/services/org/apache/camel/bean/InfinispanRemoteKeyValueRepository.json
index 7a920e4f70b2..bce4c304e5c1 100644
---
a/components/camel-infinispan/camel-infinispan/src/generated/resources/META-INF/services/org/apache/camel/bean/InfinispanRemoteKeyValueRepository.json
+++
b/components/camel-infinispan/camel-infinispan/src/generated/resources/META-INF/services/org/apache/camel/bean/InfinispanRemoteKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-infinispan",
"version": "4.23.0-SNAPSHOT",
- "properties": { "cacheName": { "index": 0, "kind": "property",
"displayName": "Cache Name", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Name of cache" }, "configuration": { "index": 1, "kind":
"property", "displayName": "Configuration", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration",
"deprecated": false, "auto [...]
+ "properties": { "cacheName": { "index": 0, "kind": "property",
"displayName": "Cache Name", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"description": "Name of cache" }, "configuration": { "index": 1, "kind":
"property", "displayName": "Configuration", "required": false, "type":
"object", "javaType":
"org.apache.camel.component.infinispan.remote.InfinispanRemoteConfiguration",
"deprecated": false, "auto [...]
}
}
diff --git
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepository.java
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepository.java
index 19dafea30149..75861c551ccc 100644
---
a/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepository.java
+++
b/components/camel-infinispan/camel-infinispan/src/main/java/org/apache/camel/component/infinispan/remote/InfinispanRemoteKeyValueRepository.java
@@ -73,6 +73,13 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
private String cacheName;
@Metadata(description = "Configuration for remote Infinispan")
private InfinispanRemoteConfiguration configuration;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
public InfinispanRemoteKeyValueRepository() {
}
@@ -90,7 +97,7 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
@ManagedOperation(description = "Get value by key")
public @Nullable Object get(String key) {
byte[] bytes = cache.get().get(key);
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -103,14 +110,14 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
} else {
previous = cache.get().put(key, serialized);
}
- return previous != null ?
KeyValueRepositoryHelper.deserialize(previous) : null;
+ return previous != null ?
KeyValueRepositoryHelper.deserialize(previous, deserializationFilter) : null;
}
@Override
@ManagedOperation(description = "Delete a key")
public @Nullable Object delete(String key) {
byte[] bytes = cache.get().remove(key);
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -139,7 +146,7 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
} else {
existing = cache.get().putIfAbsent(key, serialized);
}
- return existing != null ?
KeyValueRepositoryHelper.deserialize(existing) : null;
+ return existing != null ?
KeyValueRepositoryHelper.deserialize(existing, deserializationFilter) : null;
}
/**
@@ -156,7 +163,7 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
if (metadata == null) {
return false;
}
- Object currentObj =
KeyValueRepositoryHelper.deserialize(metadata.getValue());
+ Object currentObj =
KeyValueRepositoryHelper.deserialize(metadata.getValue(),
deserializationFilter);
if (!Objects.equals(currentObj, expectedOldValue)) {
return false;
}
@@ -180,7 +187,7 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
if (metadata == null) {
return false;
}
- Object currentObj =
KeyValueRepositoryHelper.deserialize(metadata.getValue());
+ Object currentObj =
KeyValueRepositoryHelper.deserialize(metadata.getValue(),
deserializationFilter);
if (!Objects.equals(currentObj, expectedValue)) {
return false;
}
@@ -223,6 +230,20 @@ public class InfinispanRemoteKeyValueRepository extends
ServiceSupport implement
this.configuration.setCacheContainer(cacheContainer);
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
public InfinispanRemoteManager getManager() {
return manager;
}
diff --git
a/components/camel-jpa/src/generated/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepositoryConfigurer.java
b/components/camel-jpa/src/generated/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepositoryConfigurer.java
index c7a43f4cd484..ce68724b80e6 100644
---
a/components/camel-jpa/src/generated/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepositoryConfigurer.java
+++
b/components/camel-jpa/src/generated/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepositoryConfigurer.java
@@ -23,6 +23,8 @@ public class JpaKeyValueRepositoryConfigurer extends
org.apache.camel.support.co
public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
org.apache.camel.processor.keyvalue.jpa.JpaKeyValueRepository target =
(org.apache.camel.processor.keyvalue.jpa.JpaKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "entitymanagerfactory":
case "entityManagerFactory":
target.setEntityManagerFactory(property(camelContext,
jakarta.persistence.EntityManagerFactory.class, value)); return true;
case "jointransaction":
@@ -38,6 +40,8 @@ public class JpaKeyValueRepositoryConfigurer extends
org.apache.camel.support.co
@Override
public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "entitymanagerfactory":
case "entityManagerFactory": return
jakarta.persistence.EntityManagerFactory.class;
case "jointransaction":
@@ -54,6 +58,8 @@ public class JpaKeyValueRepositoryConfigurer extends
org.apache.camel.support.co
public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
org.apache.camel.processor.keyvalue.jpa.JpaKeyValueRepository target =
(org.apache.camel.processor.keyvalue.jpa.JpaKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "entitymanagerfactory":
case "entityManagerFactory": return target.getEntityManagerFactory();
case "jointransaction":
diff --git
a/components/camel-jpa/src/generated/resources/META-INF/services/org/apache/camel/bean/JpaKeyValueRepository.json
b/components/camel-jpa/src/generated/resources/META-INF/services/org/apache/camel/bean/JpaKeyValueRepository.json
index 2ec1140fe36b..6073d5e78960 100644
---
a/components/camel-jpa/src/generated/resources/META-INF/services/org/apache/camel/bean/JpaKeyValueRepository.json
+++
b/components/camel-jpa/src/generated/resources/META-INF/services/org/apache/camel/bean/JpaKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-jpa",
"version": "4.23.0-SNAPSHOT",
- "properties": { "entityManagerFactory": { "index": 0, "kind": "property",
"displayName": "Entity Manager Factory", "required": true, "type": "object",
"javaType": "jakarta.persistence.EntityManagerFactory", "deprecated": false,
"autowired": false, "secret": false, "description": "The JPA
EntityManagerFactory to use" }, "transactionStrategy": { "index": 1, "kind":
"property", "displayName": "Transaction Strategy", "required": false, "type":
"object", "javaType": "org.apache.camel.comp [...]
+ "properties": { "entityManagerFactory": { "index": 0, "kind": "property",
"displayName": "Entity Manager Factory", "required": true, "type": "object",
"javaType": "jakarta.persistence.EntityManagerFactory", "deprecated": false,
"autowired": false, "secret": false, "description": "The JPA
EntityManagerFactory to use" }, "transactionStrategy": { "index": 1, "kind":
"property", "displayName": "Transaction Strategy", "required": false, "type":
"object", "javaType": "org.apache.camel.comp [...]
}
}
diff --git
a/components/camel-jpa/src/main/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepository.java
b/components/camel-jpa/src/main/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepository.java
index cea0eadf2443..104a7d96cad3 100644
---
a/components/camel-jpa/src/main/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepository.java
+++
b/components/camel-jpa/src/main/java/org/apache/camel/processor/keyvalue/jpa/JpaKeyValueRepository.java
@@ -83,6 +83,13 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
private boolean joinTransaction = true;
@Metadata(description = "Whether to use a shared EntityManager",
defaultValue = "false")
private boolean sharedEntityManager;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
/**
* Creates a new JPA key-value repository. The {@link
#setEntityManagerFactory(EntityManagerFactory)} must be called
@@ -122,7 +129,7 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
entityManager.flush();
rc[0] = null;
} else {
- rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue());
+ rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue(),
deserializationFilter);
}
} catch (Exception ex) {
String contextInfo = String.format(SOMETHING_WENT_WRONG,
ex.getMessage());
@@ -154,7 +161,7 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
KeyValueEntry entry = findByKey(entityManager, key);
if (entry != null) {
if (!entry.isExpired()) {
- rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue());
+ rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue(),
deserializationFilter);
}
entry.setItemValue(serializedValue);
entry.setExpiresAt(expiresAt);
@@ -196,7 +203,7 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
entityManager.flush();
rc[0] = null;
} else {
- rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue());
+ rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue(),
deserializationFilter);
entityManager.remove(entry);
entityManager.flush();
}
@@ -324,7 +331,7 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
KeyValueEntry entry = findByKey(entityManager, key);
if (entry != null && !entry.isExpired()) {
// key exists and is valid -- return existing value
- rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue());
+ rc[0] =
KeyValueRepositoryHelper.deserialize(entry.getItemValue(),
deserializationFilter);
} else if (entry != null) {
// key exists but expired -- update in place
entry.setItemValue(serializedValue);
@@ -346,7 +353,8 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
// re-read to return the existing value
try {
KeyValueEntry existing = findByKey(entityManager, key);
- rc[0] = existing != null ?
KeyValueRepositoryHelper.deserialize(existing.getItemValue()) : null;
+ rc[0] = existing != null
+ ?
KeyValueRepositoryHelper.deserialize(existing.getItemValue(),
deserializationFilter) : null;
} catch (Exception inner) {
// fall through with null
rc[0] = null;
@@ -449,6 +457,20 @@ public class JpaKeyValueRepository extends ServiceSupport
implements KeyValueRep
this.sharedEntityManager = sharedEntityManager;
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
// ---- Lifecycle ----
@Override
diff --git
a/components/camel-kafka/src/generated/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepositoryConfigurer.java
b/components/camel-kafka/src/generated/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepositoryConfigurer.java
index 3707445d3ad3..6c20fd0eb022 100644
---
a/components/camel-kafka/src/generated/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepositoryConfigurer.java
+++
b/components/camel-kafka/src/generated/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepositoryConfigurer.java
@@ -27,6 +27,8 @@ public class KafkaKeyValueRepositoryConfigurer extends
org.apache.camel.support.
case "bootstrapServers":
target.setBootstrapServers(property(camelContext, java.lang.String.class,
value)); return true;
case "consumerconfig":
case "consumerConfig": target.setConsumerConfig(property(camelContext,
java.util.Properties.class, value)); return true;
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "groupid":
case "groupId": target.setGroupId(property(camelContext,
java.lang.String.class, value)); return true;
case "maxcachesize":
@@ -49,6 +51,8 @@ public class KafkaKeyValueRepositoryConfigurer extends
org.apache.camel.support.
case "bootstrapServers": return java.lang.String.class;
case "consumerconfig":
case "consumerConfig": return java.util.Properties.class;
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "groupid":
case "groupId": return java.lang.String.class;
case "maxcachesize":
@@ -72,6 +76,8 @@ public class KafkaKeyValueRepositoryConfigurer extends
org.apache.camel.support.
case "bootstrapServers": return target.getBootstrapServers();
case "consumerconfig":
case "consumerConfig": return target.getConsumerConfig();
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "groupid":
case "groupId": return target.getGroupId();
case "maxcachesize":
diff --git
a/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/bean/KafkaKeyValueRepository.json
b/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/bean/KafkaKeyValueRepository.json
index dd228413c88e..d7c3d1900b55 100644
---
a/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/bean/KafkaKeyValueRepository.json
+++
b/components/camel-kafka/src/generated/resources/META-INF/services/org/apache/camel/bean/KafkaKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-kafka",
"version": "4.23.0-SNAPSHOT",
- "properties": { "consumerConfig": { "index": 0, "kind": "property",
"displayName": "Consumer Config", "required": false, "type": "object",
"javaType": "java.util.Properties", "deprecated": false, "autowired": false,
"secret": false, "description": "Custom properties for the Kafka consumer" },
"producerConfig": { "index": 1, "kind": "property", "displayName": "Producer
Config", "required": false, "type": "object", "javaType":
"java.util.Properties", "deprecated": false, "autowired": f [...]
+ "properties": { "consumerConfig": { "index": 0, "kind": "property",
"displayName": "Consumer Config", "required": false, "type": "object",
"javaType": "java.util.Properties", "deprecated": false, "autowired": false,
"secret": false, "description": "Custom properties for the Kafka consumer" },
"producerConfig": { "index": 1, "kind": "property", "displayName": "Producer
Config", "required": false, "type": "object", "javaType":
"java.util.Properties", "deprecated": false, "autowired": f [...]
}
}
diff --git
a/components/camel-kafka/src/main/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepository.java
b/components/camel-kafka/src/main/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepository.java
index b793689919e8..ada853fa5503 100644
---
a/components/camel-kafka/src/main/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepository.java
+++
b/components/camel-kafka/src/main/java/org/apache/camel/processor/keyvalue/kafka/KafkaKeyValueRepository.java
@@ -141,6 +141,13 @@ public class KafkaKeyValueRepository extends
ServiceSupport implements KeyValueR
private int pollDurationMs = DEFAULT_POLL_DURATION_MS;
@Metadata(description = "Whether to sync on startup only, or to continue
syncing while Camel is running.")
private boolean startupOnly;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
public KafkaKeyValueRepository() {
}
@@ -356,7 +363,7 @@ public class KafkaKeyValueRepository extends ServiceSupport
implements KeyValueR
private Object deserializeValue(byte[] data) {
// Value starts at offset 9 (1 byte action + 8 bytes expiresAt)
- return KeyValueRepositoryHelper.deserialize(data, 9, data.length - 9);
+ return KeyValueRepositoryHelper.deserialize(data, 9, data.length - 9,
deserializationFilter);
}
private long deserializeExpiresAt(byte[] data) {
@@ -678,6 +685,20 @@ public class KafkaKeyValueRepository extends
ServiceSupport implements KeyValueR
this.groupId = groupId;
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
@ManagedOperation(description = "Number of sync events received from the
kafka topic")
public long getCacheCounter() {
return cacheCounter.get();
diff --git
a/components/camel-redis/src/generated/java/org/apache/camel/component/redis/RedisKeyValueRepositoryConfigurer.java
b/components/camel-redis/src/generated/java/org/apache/camel/component/redis/RedisKeyValueRepositoryConfigurer.java
index 63e66174255e..f98b33c44a77 100644
---
a/components/camel-redis/src/generated/java/org/apache/camel/component/redis/RedisKeyValueRepositoryConfigurer.java
+++
b/components/camel-redis/src/generated/java/org/apache/camel/component/redis/RedisKeyValueRepositoryConfigurer.java
@@ -23,6 +23,8 @@ public class RedisKeyValueRepositoryConfigurer extends
org.apache.camel.support.
public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
org.apache.camel.component.redis.RedisKeyValueRepository target =
(org.apache.camel.component.redis.RedisKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "endpoint": target.setEndpoint(property(camelContext,
java.lang.String.class, value)); return true;
case "keyprefix":
case "keyPrefix": target.setKeyPrefix(property(camelContext,
java.lang.String.class, value)); return true;
@@ -34,6 +36,8 @@ public class RedisKeyValueRepositoryConfigurer extends
org.apache.camel.support.
@Override
public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "endpoint": return java.lang.String.class;
case "keyprefix":
case "keyPrefix": return java.lang.String.class;
@@ -46,6 +50,8 @@ public class RedisKeyValueRepositoryConfigurer extends
org.apache.camel.support.
public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
org.apache.camel.component.redis.RedisKeyValueRepository target =
(org.apache.camel.component.redis.RedisKeyValueRepository) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "endpoint": return target.getEndpoint();
case "keyprefix":
case "keyPrefix": return target.getKeyPrefix();
diff --git
a/components/camel-redis/src/generated/resources/META-INF/services/org/apache/camel/bean/RedisKeyValueRepository.json
b/components/camel-redis/src/generated/resources/META-INF/services/org/apache/camel/bean/RedisKeyValueRepository.json
index 35fad0e6c77f..c8fc56830883 100644
---
a/components/camel-redis/src/generated/resources/META-INF/services/org/apache/camel/bean/RedisKeyValueRepository.json
+++
b/components/camel-redis/src/generated/resources/META-INF/services/org/apache/camel/bean/RedisKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-redis",
"version": "4.23.0-SNAPSHOT",
- "properties": { "redisson": { "index": 0, "kind": "property",
"displayName": "Redisson", "label": "advanced", "required": false, "type":
"object", "javaType": "org.redisson.api.RedissonClient", "deprecated": false,
"autowired": false, "secret": false, "description": "To use an existing
Redisson client to connect to Redis server" }, "endpoint": { "index": 1,
"kind": "property", "displayName": "Endpoint", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": [...]
+ "properties": { "redisson": { "index": 0, "kind": "property",
"displayName": "Redisson", "label": "advanced", "required": false, "type":
"object", "javaType": "org.redisson.api.RedissonClient", "deprecated": false,
"autowired": false, "secret": false, "description": "To use an existing
Redisson client to connect to Redis server" }, "endpoint": { "index": 1,
"kind": "property", "displayName": "Endpoint", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": [...]
}
}
diff --git
a/components/camel-redis/src/main/java/org/apache/camel/component/redis/RedisKeyValueRepository.java
b/components/camel-redis/src/main/java/org/apache/camel/component/redis/RedisKeyValueRepository.java
index 687194885cdb..eca75084e86c 100644
---
a/components/camel-redis/src/main/java/org/apache/camel/component/redis/RedisKeyValueRepository.java
+++
b/components/camel-redis/src/main/java/org/apache/camel/component/redis/RedisKeyValueRepository.java
@@ -67,6 +67,13 @@ public class RedisKeyValueRepository extends ServiceSupport
implements KeyValueR
private String endpoint;
@Metadata(description = "Key prefix used to namespace entries in Redis",
defaultValue = "camel-kvr:")
private String keyPrefix = "camel-kvr:";
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
public RedisKeyValueRepository() {
}
@@ -96,7 +103,7 @@ public class RedisKeyValueRepository extends ServiceSupport
implements KeyValueR
public @Nullable Object get(String key) {
RBucket<byte[]> bucket = redisson.getBucket(toRedisKey(key),
ByteArrayCodec.INSTANCE);
byte[] bytes = bucket.get();
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -110,7 +117,7 @@ public class RedisKeyValueRepository extends ServiceSupport
implements KeyValueR
} else {
previous = bucket.getAndSet(serialized);
}
- return previous != null ?
KeyValueRepositoryHelper.deserialize(previous) : null;
+ return previous != null ?
KeyValueRepositoryHelper.deserialize(previous, deserializationFilter) : null;
}
@Override
@@ -118,7 +125,7 @@ public class RedisKeyValueRepository extends ServiceSupport
implements KeyValueR
public @Nullable Object delete(String key) {
RBucket<byte[]> bucket = redisson.getBucket(toRedisKey(key),
ByteArrayCodec.INSTANCE);
byte[] bytes = bucket.getAndDelete();
- return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes) :
null;
+ return bytes != null ? KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter) : null;
}
@Override
@@ -160,7 +167,7 @@ public class RedisKeyValueRepository extends ServiceSupport
implements KeyValueR
}
// Key already existed; return the current value
byte[] existing = bucket.get();
- return existing != null ?
KeyValueRepositoryHelper.deserialize(existing) : null;
+ return existing != null ?
KeyValueRepositoryHelper.deserialize(existing, deserializationFilter) : null;
}
/**
@@ -223,6 +230,20 @@ public class RedisKeyValueRepository extends
ServiceSupport implements KeyValueR
this.redisson = redisson;
}
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
+
// ---- Lifecycle ----
@Override
diff --git
a/components/camel-sql/src/generated/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepositoryConfigurer.java
b/components/camel-sql/src/generated/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepositoryConfigurer.java
index 377981ec7fcc..ee2a16c8c1ad 100644
---
a/components/camel-sql/src/generated/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepositoryConfigurer.java
+++
b/components/camel-sql/src/generated/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepositoryConfigurer.java
@@ -35,6 +35,8 @@ public class JdbcKeyValueRepositoryConfigurer extends
org.apache.camel.support.c
case "deleteIfValueString":
target.setDeleteIfValueString(property(camelContext, java.lang.String.class,
value)); return true;
case "deletestring":
case "deleteString": target.setDeleteString(property(camelContext,
java.lang.String.class, value)); return true;
+ case "deserializationfilter":
+ case "deserializationFilter":
target.setDeserializationFilter(property(camelContext, java.lang.String.class,
value)); return true;
case "insertstring":
case "insertString": target.setInsertString(property(camelContext,
java.lang.String.class, value)); return true;
case "jdbctemplate":
@@ -70,6 +72,8 @@ public class JdbcKeyValueRepositoryConfigurer extends
org.apache.camel.support.c
case "deleteIfValueString": return java.lang.String.class;
case "deletestring":
case "deleteString": return java.lang.String.class;
+ case "deserializationfilter":
+ case "deserializationFilter": return java.lang.String.class;
case "insertstring":
case "insertString": return java.lang.String.class;
case "jdbctemplate":
@@ -106,6 +110,8 @@ public class JdbcKeyValueRepositoryConfigurer extends
org.apache.camel.support.c
case "deleteIfValueString": return target.getDeleteIfValueString();
case "deletestring":
case "deleteString": return target.getDeleteString();
+ case "deserializationfilter":
+ case "deserializationFilter": return target.getDeserializationFilter();
case "insertstring":
case "insertString": return target.getInsertString();
case "jdbctemplate":
diff --git
a/components/camel-sql/src/generated/resources/META-INF/services/org/apache/camel/bean/JdbcKeyValueRepository.json
b/components/camel-sql/src/generated/resources/META-INF/services/org/apache/camel/bean/JdbcKeyValueRepository.json
index 07bc8667d665..2042d4a6633e 100644
---
a/components/camel-sql/src/generated/resources/META-INF/services/org/apache/camel/bean/JdbcKeyValueRepository.json
+++
b/components/camel-sql/src/generated/resources/META-INF/services/org/apache/camel/bean/JdbcKeyValueRepository.json
@@ -10,7 +10,7 @@
"groupId": "org.apache.camel",
"artifactId": "camel-sql",
"version": "4.23.0-SNAPSHOT",
- "properties": { "jdbcTemplate": { "index": 0, "kind": "property",
"displayName": "Jdbc Template", "required": true, "type": "object", "javaType":
"org.springframework.jdbc.core.JdbcTemplate", "deprecated": false, "autowired":
false, "secret": false, "description": "The Spring JdbcTemplate to use for
connecting to the database" }, "transactionTemplate": { "index": 1, "kind":
"property", "displayName": "Transaction Template", "required": true, "type":
"object", "javaType": "org.springf [...]
+ "properties": { "jdbcTemplate": { "index": 0, "kind": "property",
"displayName": "Jdbc Template", "required": true, "type": "object", "javaType":
"org.springframework.jdbc.core.JdbcTemplate", "deprecated": false, "autowired":
false, "secret": false, "description": "The Spring JdbcTemplate to use for
connecting to the database" }, "transactionTemplate": { "index": 1, "kind":
"property", "displayName": "Transaction Template", "required": true, "type":
"object", "javaType": "org.springf [...]
}
}
diff --git
a/components/camel-sql/src/main/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepository.java
b/components/camel-sql/src/main/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepository.java
index 47eadd3efd77..f05b43b16a2d 100644
---
a/components/camel-sql/src/main/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepository.java
+++
b/components/camel-sql/src/main/java/org/apache/camel/processor/keyvalue/jdbc/JdbcKeyValueRepository.java
@@ -113,6 +113,13 @@ public class JdbcKeyValueRepository extends ServiceSupport
implements KeyValueRe
private String updateIfValueString = DEFAULT_UPDATE_IF_VALUE_STRING;
@Metadata(label = "advanced", description = "SQL query to use for
conditional delete (CAS delete)")
private String deleteIfValueString = DEFAULT_DELETE_IF_VALUE_STRING;
+ @Metadata(label = "advanced,security",
+ description = "Sets an ObjectInputFilter pattern
(jdk.serialFilter syntax) applied when deserializing"
+ + " values read back from the repository. 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. Widen"
+ + " this pattern when storing instances of your
own classes in the repository.")
+ private String deserializationFilter;
/**
* Creates a new JDBC key-value repository. A {@link DataSource} or {@link
JdbcTemplate} must be set before
@@ -373,7 +380,7 @@ public class JdbcKeyValueRepository extends ServiceSupport
implements KeyValueRe
expired[0] = true;
return null;
}
- return KeyValueRepositoryHelper.deserialize(bytes);
+ return KeyValueRepositoryHelper.deserialize(bytes,
deserializationFilter);
}, key);
if (expired[0]) {
jdbcTemplate.update(getDeleteString(), key);
@@ -518,4 +525,18 @@ public class JdbcKeyValueRepository extends ServiceSupport
implements KeyValueRe
public void setDeleteIfValueString(String deleteIfValueString) {
this.deleteIfValueString = deleteIfValueString;
}
+
+ public String getDeserializationFilter() {
+ return deserializationFilter;
+ }
+
+ /**
+ * Sets an {@link java.io.ObjectInputFilter} pattern (same syntax as
{@code jdk.serialFilter}) applied when
+ * deserializing values read back from the repository. When not set, the
JVM-wide {@code jdk.serialFilter} is used
+ * if present, otherwise a conservative default filter is applied. Widen
this pattern when storing instances of your
+ * own classes in the repository.
+ */
+ public void setDeserializationFilter(String deserializationFilter) {
+ this.deserializationFilter = deserializationFilter;
+ }
}
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
index 13d3ff696456..87c143a0afb7 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/KeyValueRepositoryHelper.java
@@ -19,6 +19,7 @@ package org.apache.camel.support;
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 java.nio.ByteBuffer;
@@ -33,8 +34,12 @@ import org.apache.camel.RuntimeCamelException;
* try/catch boilerplate in every implementation.
* <p/>
* <b>Security note:</b> These methods use plain Java serialization
- * ({@link ObjectOutputStream}/{@link ObjectInputStream}). The stored data is
trusted — it was written by the same
- * application instance or cluster. Do not expose a repository's raw byte
store to untrusted input.
+ * ({@link ObjectOutputStream}/{@link ObjectInputStream}). The byte store
backing a repository is shared infrastructure
+ * (a Redis instance, a database table, a Kafka topic, a data grid) and is not
necessarily writable only by the
+ * application that owns it. Every {@code deserialize} method therefore
installs a JEP-290 {@link ObjectInputFilter}
+ * resolved by {@link DeserializationFilterHelper}, in the same way the
aggregation repositories do. The default filter
+ * only allows standard Java and Apache Camel types, so applications that
store their own classes must widen it through
+ * the {@code deserializationFilter} option of the repository they use.
*
* @since 4.23
*/
@@ -75,21 +80,48 @@ public final class KeyValueRepositoryHelper {
}
/**
- * Deserializes a byte array back into an object using Java object
serialization.
+ * Deserializes a byte array back into an object using Java object
serialization, applying the default
+ * deserialization filter.
*
* @param bytes the bytes to deserialize
* @return the deserialized object
- * @throws RuntimeCamelException if deserialization fails
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
*/
public static Object deserialize(byte[] bytes) {
- try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
- ObjectInputStream ois = new ObjectInputStream(bis)) {
- return ois.readObject();
+ return deserialize(bytes, null);
+ }
+
+ /**
+ * Deserializes a byte array back into an object using Java object
serialization.
+ *
+ * @param bytes the bytes to deserialize
+ * @param deserializationFilter the filter pattern to apply, or null to
use the JVM-wide filter or the Camel
+ * default
+ * @return the deserialized object
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
+ */
+ public static Object deserialize(byte[] bytes, String
deserializationFilter) {
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
+ return doDeserialize(bis, deserializationFilter);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeCamelException("Failed to deserialize value", e);
}
}
+ /**
+ * Deserializes an object from a portion of a byte array using Java object
serialization, applying the default
+ * deserialization filter. Useful when the serialized data starts at an
offset (e.g. after a protocol header).
+ *
+ * @param bytes the byte array containing the serialized
data
+ * @param offset the start offset within the array
+ * @param length the number of bytes to read
+ * @return the deserialized object
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
+ */
+ public static Object deserialize(byte[] bytes, int offset, int length) {
+ return deserialize(bytes, offset, length, null);
+ }
+
/**
* Deserializes an object from a portion of a byte array using Java object
serialization. Useful when the serialized
* data starts at an offset (e.g. after a protocol header).
@@ -97,29 +129,52 @@ public final class KeyValueRepositoryHelper {
* @param bytes the byte array containing the serialized
data
* @param offset the start offset within the array
* @param length the number of bytes to read
+ * @param deserializationFilter the filter pattern to apply, or null to
use the JVM-wide filter or the Camel
+ * default
* @return the deserialized object
- * @throws RuntimeCamelException if deserialization fails
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
*/
- public static Object deserialize(byte[] bytes, int offset, int length) {
- try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes,
offset, length);
- ObjectInputStream ois = new ObjectInputStream(bis)) {
- return ois.readObject();
+ public static Object deserialize(byte[] bytes, int offset, int length,
String deserializationFilter) {
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes,
offset, length)) {
+ return doDeserialize(bis, deserializationFilter);
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeCamelException("Failed to deserialize value", e);
}
}
+ /**
+ * Deserializes an object from a {@link ByteBuffer} using Java object
serialization, applying the default
+ * deserialization filter. The buffer's remaining bytes are consumed.
+ *
+ * @param buffer the ByteBuffer containing the serialized
bytes
+ * @return the deserialized object
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
+ */
+ public static Object deserialize(ByteBuffer buffer) {
+ return deserialize(buffer, null);
+ }
+
/**
* Deserializes an object from a {@link ByteBuffer} using Java object
serialization. The buffer's remaining bytes
* are consumed.
*
* @param buffer the ByteBuffer containing the serialized
bytes
+ * @param deserializationFilter the filter pattern to apply, or null to
use the JVM-wide filter or the Camel
+ * default
* @return the deserialized object
- * @throws RuntimeCamelException if deserialization fails
+ * @throws RuntimeCamelException if deserialization fails or the filter
rejects the stream
*/
- public static Object deserialize(ByteBuffer buffer) {
+ public static Object deserialize(ByteBuffer buffer, String
deserializationFilter) {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
- return deserialize(bytes);
+ return deserialize(bytes, deserializationFilter);
+ }
+
+ private static Object doDeserialize(ByteArrayInputStream bis, String
deserializationFilter)
+ throws IOException, ClassNotFoundException {
+ try (ObjectInputStream ois = new ObjectInputStream(bis)) {
+
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
+ return ois.readObject();
+ }
}
}
diff --git
a/core/camel-support/src/test/java/org/apache/camel/support/KeyValueRepositoryHelperTest.java
b/core/camel-support/src/test/java/org/apache/camel/support/KeyValueRepositoryHelperTest.java
index 0977b39c41b0..8ae3f86c6c3c 100644
---
a/core/camel-support/src/test/java/org/apache/camel/support/KeyValueRepositoryHelperTest.java
+++
b/core/camel-support/src/test/java/org/apache/camel/support/KeyValueRepositoryHelperTest.java
@@ -16,8 +16,10 @@
*/
package org.apache.camel.support;
+import java.io.InvalidClassException;
import java.io.Serial;
import java.io.Serializable;
+import java.net.URI;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
@@ -271,6 +273,78 @@ class KeyValueRepositoryHelperTest {
assertThat(result).isNull();
}
+ //
-------------------------------------------------------------------------
+ // Deserialization filter
+ //
-------------------------------------------------------------------------
+
+ @Test
+ void testDefaultFilterRejectsDeniedClass() {
+ // java.net.** is denied by the default filter as those classes may
perform network I/O in hashCode/equals
+ byte[] bytes =
KeyValueRepositoryHelper.serialize(URI.create("http://localhost:8080/camel"));
+
+ assertThatThrownBy(() -> KeyValueRepositoryHelper.deserialize(bytes))
+ .isInstanceOf(RuntimeCamelException.class)
+ .hasMessageContaining("Failed to deserialize value")
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+ }
+
+ @Test
+ void testDefaultFilterAllowsCamelAndJavaClasses() {
+ ComplexPayload original = new ComplexPayload("order-1", 1,
List.of("item-a"));
+
+ byte[] bytes = KeyValueRepositoryHelper.serialize(original);
+
+
assertThat(KeyValueRepositoryHelper.deserialize(bytes)).isEqualTo(original);
+ }
+
+ @Test
+ void testCustomFilterCanWidenTheAllowList() {
+ URI original = URI.create("http://localhost:8080/camel");
+ byte[] bytes = KeyValueRepositoryHelper.serialize(original);
+
+ Object result = KeyValueRepositoryHelper.deserialize(bytes,
"java.net.**;java.**;!*");
+
+ assertThat(result).isEqualTo(original);
+ }
+
+ @Test
+ void testCustomFilterCanNarrowTheAllowList() {
+ // note: java.lang.String is written as TC_STRING and never passes
through the filter, so use a boxed number
+ byte[] bytes = KeyValueRepositoryHelper.serialize(42);
+
+ assertThatThrownBy(() -> KeyValueRepositoryHelper.deserialize(bytes,
"!*"))
+ .isInstanceOf(RuntimeCamelException.class)
+ .hasMessageContaining("Failed to deserialize value")
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+ }
+
+ @Test
+ void testCustomFilterAppliedOnOffsetVariant() {
+ URI original = URI.create("http://localhost:8080/camel");
+ byte[] bytes = KeyValueRepositoryHelper.serialize(original);
+
+ assertThatThrownBy(() -> KeyValueRepositoryHelper.deserialize(bytes,
0, bytes.length))
+ .isInstanceOf(RuntimeCamelException.class)
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+
+ assertThat(KeyValueRepositoryHelper.deserialize(bytes, 0,
bytes.length, "java.net.**;java.**;!*"))
+ .isEqualTo(original);
+ }
+
+ @Test
+ void testCustomFilterAppliedOnByteBufferVariant() {
+ URI original = URI.create("http://localhost:8080/camel");
+
+ assertThatThrownBy(() -> KeyValueRepositoryHelper.deserialize(
+ KeyValueRepositoryHelper.serializeToByteBuffer(original)))
+ .isInstanceOf(RuntimeCamelException.class)
+ .hasRootCauseInstanceOf(InvalidClassException.class);
+
+ assertThat(KeyValueRepositoryHelper.deserialize(
+ KeyValueRepositoryHelper.serializeToByteBuffer(original),
"java.net.**;java.**;!*"))
+ .isEqualTo(original);
+ }
+
//
-------------------------------------------------------------------------
// Helper types
//
-------------------------------------------------------------------------
diff --git a/docs/user-manual/modules/ROOT/pages/key-value-repository.adoc
b/docs/user-manual/modules/ROOT/pages/key-value-repository.adoc
index 8fdefdbc4478..1b7e44909e35 100644
--- a/docs/user-manual/modules/ROOT/pages/key-value-repository.adoc
+++ b/docs/user-manual/modules/ROOT/pages/key-value-repository.adoc
@@ -260,6 +260,41 @@ repo.setCacheName("camel-kvr"); // optional
Fully atomic CAS operations including `replace` with TTL in a single native
call.
+== Serialization
+
+The persistent backends (JDBC, JPA, Cassandra, Kafka, Hazelcast, Redis and
Infinispan) store values
+as bytes produced by plain Java serialization, via the shared
`KeyValueRepositoryHelper`. Values must
+therefore implement `java.io.Serializable`.
+
+Because the byte store behind those backends is shared infrastructure -- a
database table, a Kafka
+topic, a Redis instance, a data grid -- reading an entry back is a
deserialization of data that Camel
+did not necessarily write. Every read therefore installs a JEP-290
`java.io.ObjectInputFilter`,
+exactly as the aggregation repositories do.
+
+By default the filter denies `java.net.*` and otherwise allows `java.*`,
`javax.*` and
+`org.apache.camel.*`, with the JEP-290 graph-shape limits `maxdepth`,
`maxrefs` and `maxbytes` applied
+as defense-in-depth. If the JVM-wide `jdk.serialFilter` system property is
set, it takes precedence
+over the Camel default.
+
+This means that storing instances of your *own* classes requires widening the
filter through the
+`deserializationFilter` option, which every persistent backend exposes:
+
+[source,java]
+----
+RedisKeyValueRepository repo = new RedisKeyValueRepository("localhost:6379");
+repo.setDeserializationFilter("com.mycompany.model.**;java.**;javax.**;org.apache.camel.**;!*");
+----
+
+The pattern uses the same syntax as `jdk.serialFilter`. Keep it as narrow as
possible: a permissive
+pattern such as `*` re-opens the repository to arbitrary gadget classes.
+
+The same applies indirectly when a persistent backend is used through
`KeyValueAggregationRepository`:
+the aggregated exchange is stored as a `DefaultExchangeHolder`, so the message
body and any serialized
+header types must be covered by the filter as well.
+
+The in-memory backends (Memory, Caffeine, Ehcache, JCache) keep object
references and do not
+serialize, so they have no `deserializationFilter` option.
+
== Usage with Camel Patterns
=== Single backend, multiple patterns