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 85ac83ef56c6 CAMEL-24296: camel-support - apply a JEP-290
deserialization filter on CamelObjectInputStream by default
85ac83ef56c6 is described below
commit 85ac83ef56c64b45e1f2dc7debee3866a8b03e39
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Aug 6 15:38:58 2026 +0200
CAMEL-24296: camel-support - apply a JEP-290 deserialization filter on
CamelObjectInputStream by default
CamelObjectInputStream now installs a JEP-290 ObjectInputFilter by default
as a
defense-in-depth measure against unsafe deserialization. When no explicit
filter
pattern is supplied, the JVM-wide jdk.serialFilter is honoured if set,
otherwise
Camel's default allow-list is applied.
HttpHelper updated to use the new 3-arg constructor. Upgrade guide entry
added.
Closes #25378
Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
.../org/apache/camel/http/common/HttpHelper.java | 3 +-
.../camel/support/CamelObjectInputStream.java | 27 ++++++++
.../camel/support/CamelObjectInputStreamTest.java | 78 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 15 +++++
4 files changed, 121 insertions(+), 2 deletions(-)
diff --git
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
index a95af16da5f0..93195b91d95b 100644
---
a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
+++
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpHelper.java
@@ -142,8 +142,7 @@ public final class HttpHelper {
}
Object answer;
- ObjectInputStream ois = new CamelObjectInputStream(is, context);
-
ois.setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
+ ObjectInputStream ois = new CamelObjectInputStream(is, context,
deserializationFilter);
try {
answer = ois.readObject();
} finally {
diff --git
a/core/camel-support/src/main/java/org/apache/camel/support/CamelObjectInputStream.java
b/core/camel-support/src/main/java/org/apache/camel/support/CamelObjectInputStream.java
index 927160ed3a8a..c87041491901 100644
---
a/core/camel-support/src/main/java/org/apache/camel/support/CamelObjectInputStream.java
+++
b/core/camel-support/src/main/java/org/apache/camel/support/CamelObjectInputStream.java
@@ -18,21 +18,48 @@ package org.apache.camel.support;
import java.io.IOException;
import java.io.InputStream;
+import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import org.apache.camel.CamelContext;
+/**
+ * An {@link ObjectInputStream} that resolves classes against the Camel
application classloader and installs a JEP-290
+ * {@link ObjectInputFilter} while reading, as a defense-in-depth measure
against unsafe deserialization.
+ *
+ * <p>
+ * As this is the shared stream used by Camel deserialization consumers, the
filter is applied by default so that every
+ * caller inherits it. When no explicit pattern is supplied the JVM-wide
{@code jdk.serialFilter} is honoured if set,
+ * otherwise {@link
DeserializationFilterHelper#DEFAULT_DESERIALIZATION_FILTER} is applied.
+ */
public class CamelObjectInputStream extends ObjectInputStream {
private final ClassLoader classLoader;
public CamelObjectInputStream(InputStream in, CamelContext context) throws
IOException {
+ this(in, context, null);
+ }
+
+ /**
+ * Creates a {@link CamelObjectInputStream} that applies a JEP-290 {@link
ObjectInputFilter} while reading.
+ *
+ * @param in the input stream to read from
+ * @param context the camel context used to resolve the
application classloader; may be {@code null}
+ * @param deserializationFilter an {@link ObjectInputFilter} pattern
(same syntax as {@code jdk.serialFilter}) to
+ * apply; when {@code null} or blank the
JVM-wide {@code jdk.serialFilter} is used if
+ * present, otherwise
+ * {@link
DeserializationFilterHelper#DEFAULT_DESERIALIZATION_FILTER} is applied
+ * @throws IOException if an I/O error occurs while reading the
stream header
+ * @since 4.22
+ */
+ public CamelObjectInputStream(InputStream in, CamelContext context, String
deserializationFilter) throws IOException {
super(in);
if (context != null) {
this.classLoader = context.getApplicationContextClassLoader();
} else {
this.classLoader = null;
}
+
setObjectInputFilter(DeserializationFilterHelper.resolveDeserializationFilter(deserializationFilter));
}
@Override
diff --git
a/core/camel-support/src/test/java/org/apache/camel/support/CamelObjectInputStreamTest.java
b/core/camel-support/src/test/java/org/apache/camel/support/CamelObjectInputStreamTest.java
new file mode 100644
index 000000000000..bcbf8095dcd0
--- /dev/null
+++
b/core/camel-support/src/test/java/org/apache/camel/support/CamelObjectInputStreamTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectOutputStream;
+import java.net.InetSocketAddress;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CamelObjectInputStreamTest {
+
+ @Test
+ void defaultFilterAllowsStandardTypes() throws Exception {
+ assertEquals("hello camel", deserialize(serialize("hello camel")));
+ }
+
+ @Test
+ void defaultFilterRejectsClassOutsideAllowList() throws Exception {
+ byte[] data =
serialize(InetSocketAddress.createUnresolved("example.com", 8080));
+ InvalidClassException ex = assertThrows(InvalidClassException.class,
() -> deserialize(data));
+ assertTrue(ex.getMessage().contains("REJECTED"), ex.getMessage());
+ }
+
+ @Test
+ void blankFilterFallsBackToDefault() throws Exception {
+ byte[] data =
serialize(InetSocketAddress.createUnresolved("example.com", 8080));
+ InvalidClassException ex = assertThrows(InvalidClassException.class,
() -> deserialize(data, " "));
+ assertTrue(ex.getMessage().contains("REJECTED"), ex.getMessage());
+ }
+
+ @Test
+ void explicitFilterCanAllowOtherwiseDeniedClass() throws Exception {
+ InetSocketAddress address =
InetSocketAddress.createUnresolved("example.com", 8080);
+ assertEquals(address, deserialize(serialize(address), "java.**;!*"));
+ }
+
+ private static byte[] serialize(Object value) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
+ oos.writeObject(value);
+ }
+ return bos.toByteArray();
+ }
+
+ private static Object deserialize(byte[] data) throws IOException,
ClassNotFoundException {
+ try (CamelObjectInputStream ois = new CamelObjectInputStream(new
ByteArrayInputStream(data), null)) {
+ return ois.readObject();
+ }
+ }
+
+ private static Object deserialize(byte[] data, String filter) throws
IOException, ClassNotFoundException {
+ try (CamelObjectInputStream ois = new CamelObjectInputStream(new
ByteArrayInputStream(data), null, filter)) {
+ return ois.readObject();
+ }
+ }
+}
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 c79eee332eb9..f4a459dd7e75 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
@@ -55,6 +55,21 @@ recipient; aligning it with `toD` / `enrich` is deferred to
a follow-up.
If you need a placeholder resolved by `toD` / `enrich`, keep it in the route's
endpoint URI rather
than in the message.
+=== camel-support - CamelObjectInputStream applies a deserialization filter by
default
+
+`CamelObjectInputStream` (the shared stream used by Camel's Java-object
deserialization paths, such
+as the HTTP components) now installs a JEP-290 `java.io.ObjectInputFilter`
while reading, as a
+defense-in-depth measure against unsafe deserialization. When no explicit
filter pattern is
+supplied, the JVM-wide `jdk.serialFilter` is honoured if set, otherwise
Camel's default allow-list
+(`DeserializationFilterHelper.DEFAULT_DESERIALIZATION_FILTER`) is applied,
which permits standard
+Java and Apache Camel types, denies `java.net.**`, and enforces graph-shape
limits.
+
+The built-in HTTP deserialization path already applied this filter, so most
users are unaffected.
+Code that constructs `CamelObjectInputStream` directly and deserializes types
outside the default
+allow-list must pass an explicit filter pattern to the new
+`CamelObjectInputStream(InputStream, CamelContext, String)` constructor (or
configure
+`jdk.serialFilter`) to permit them.
+
=== camel-jbang
The Camel JBang CLI (Camel CLI) and TUI have been promoted from _Preview_ to
_Stable_ support level.