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/04/08 12:29:57 UTC
(camel) branch ci-issue-CAMEL-23297 created (now b6ad107b94cd)
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a change to branch ci-issue-CAMEL-23297
in repository https://gitbox.apache.org/repos/asf/camel.git
at b6ad107b94cd CAMEL-23297: Add deserialization filtering to camel-netty converters and codecs
This branch includes the following new commits:
new b6ad107b94cd CAMEL-23297: Add deserialization filtering to camel-netty converters and codecs
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel) 01/01: CAMEL-23297: Add deserialization filtering to camel-netty converters and codecs
Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch ci-issue-CAMEL-23297
in repository https://gitbox.apache.org/repos/asf/camel.git
commit b6ad107b94cd99c395e435e974c1fd8cad0f12b4
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Wed Apr 8 14:29:39 2026 +0200
CAMEL-23297: Add deserialization filtering to camel-netty converters and codecs
- NettyConverter.toObjectInput(): Apply default ObjectInputFilter restricting
to java.**, javax.**, org.apache.camel.** (respects JVM-wide filter if set)
- ObjectDecoder: Reimplement with ObjectInputFilter support, compatible with
Netty's CompactObjectOutputStream wire format. Accepts optional
deserializationFilter parameter. Logs warning when no filter is configured.
- DatagramPacketObjectDecoder: Add constructor accepting deserializationFilter,
passed through to ObjectDecoder.
Co-Authored-By: Claude Opus 4.6 (1M context) <no...@anthropic.com>
Signed-off-by: Andrea Cosentino <an...@gmail.com>
---
.../camel/component/netty/NettyConverter.java | 21 +++-
.../netty/codec/DatagramPacketObjectDecoder.java | 6 +-
.../camel/component/netty/codec/ObjectDecoder.java | 106 ++++++++++++++++++++-
3 files changed, 127 insertions(+), 6 deletions(-)
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
index 6b1c54b4041e..b06cb7addc08 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/NettyConverter.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.netty;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
+import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.nio.charset.StandardCharsets;
@@ -34,12 +35,21 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import org.apache.camel.Converter;
import org.apache.camel.Exchange;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* A set of converter methods for working with Netty types
*/
@Converter(generateLoader = true)
public final class NettyConverter {
+ private static final Logger LOG = LoggerFactory.getLogger(NettyConverter.class);
+
+ /**
+ * Default deserialization filter that restricts which classes can be deserialized. Allows standard Java types and
+ * Apache Camel types. Can be overridden via the JVM system property {@code jdk.serialFilter}.
+ */
+ static final String DEFAULT_DESERIALIZATION_FILTER = "java.**;javax.**;org.apache.camel.**;!*";
private NettyConverter() {
//Utility Class
@@ -79,7 +89,16 @@ public final class NettyConverter {
@Converter
public static ObjectInput toObjectInput(ByteBuf buffer, Exchange exchange) throws IOException {
InputStream is = toInputStream(buffer, exchange);
- return new ObjectInputStream(is);
+ ObjectInputStream ois = new ObjectInputStream(is);
+ ObjectInputFilter jvmFilter = ObjectInputFilter.Config.getSerialFilter();
+ if (jvmFilter != null) {
+ ois.setObjectInputFilter(jvmFilter);
+ } else {
+ LOG.debug("No JVM-wide deserialization filter set, applying default Camel filter: {}",
+ DEFAULT_DESERIALIZATION_FILTER);
+ ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER));
+ }
+ return ois;
}
@Converter
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/DatagramPacketObjectDecoder.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/DatagramPacketObjectDecoder.java
index 20a205b6ea20..47b7f5f6f532 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/DatagramPacketObjectDecoder.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/DatagramPacketObjectDecoder.java
@@ -31,7 +31,11 @@ public class DatagramPacketObjectDecoder extends MessageToMessageDecoder<Address
private final ObjectDecoder delegateDecoder;
public DatagramPacketObjectDecoder(ClassResolver resolver) {
- delegateDecoder = new ObjectDecoder(resolver);
+ this(resolver, null);
+ }
+
+ public DatagramPacketObjectDecoder(ClassResolver resolver, String deserializationFilter) {
+ delegateDecoder = new ObjectDecoder(resolver, deserializationFilter);
}
@Override
diff --git a/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/ObjectDecoder.java b/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/ObjectDecoder.java
index 0a1aca75c01e..c1508fe8cd90 100644
--- a/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/ObjectDecoder.java
+++ b/components/camel-netty/src/main/java/org/apache/camel/component/netty/codec/ObjectDecoder.java
@@ -16,22 +16,120 @@
*/
package org.apache.camel.component.netty.codec;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputFilter;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.serialization.ClassResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * Just expose the decode method for DatagramPacketObjectDecoder to use
+ * Decodes Java-serialized objects from Netty frames with optional {@link ObjectInputFilter} support.
+ * <p>
+ * Compatible with Netty's {@code ObjectEncoder} compact wire format. When a {@code deserializationFilter} is provided,
+ * only classes matching the filter pattern will be allowed during deserialization.
*/
-public class ObjectDecoder extends io.netty.handler.codec.serialization.ObjectDecoder {
+public class ObjectDecoder extends LengthFieldBasedFrameDecoder {
+ private static final Logger LOG = LoggerFactory.getLogger(ObjectDecoder.class);
+ private static final int DEFAULT_MAX_OBJECT_SIZE = 1048576;
+
+ // Matches Netty's CompactObjectOutputStream type constants
+ private static final int TYPE_FAT_DESCRIPTOR = 0;
+ private static final int TYPE_THIN_DESCRIPTOR = 1;
+
+ private final ClassResolver classResolver;
+ private final String deserializationFilter;
public ObjectDecoder(ClassResolver classResolver) {
- super(classResolver);
+ this(classResolver, null);
+ }
+
+ public ObjectDecoder(ClassResolver classResolver, String deserializationFilter) {
+ super(DEFAULT_MAX_OBJECT_SIZE, 0, 4, 0, 4);
+ this.classResolver = classResolver;
+ this.deserializationFilter = deserializationFilter;
+ if (deserializationFilter == null) {
+ LOG.warn("ObjectDecoder created without a deserialization filter."
+ + " Unrestricted deserialization of network data is a security risk."
+ + " Consider setting a deserializationFilter to restrict allowed classes.");
+ }
}
@Override
public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
- return super.decode(ctx, in);
+ ByteBuf frame = (ByteBuf) super.decode(ctx, in);
+ if (frame == null) {
+ return null;
+ }
+ ObjectInputStream ois = new CompactFilteringObjectInputStream(
+ new ByteBufInputStream(frame, true), classResolver);
+ if (deserializationFilter != null) {
+ ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter(deserializationFilter));
+ }
+ try {
+ return ois.readObject();
+ } finally {
+ ois.close();
+ }
}
+ /**
+ * ObjectInputStream that understands Netty's compact class descriptor wire format (compatible with
+ * {@code CompactObjectOutputStream}) and resolves classes via a Netty {@link ClassResolver}.
+ */
+ private static class CompactFilteringObjectInputStream extends ObjectInputStream {
+ private final ClassResolver classResolver;
+
+ CompactFilteringObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException {
+ super(in);
+ this.classResolver = classResolver;
+ }
+
+ @Override
+ protected void readStreamHeader() throws IOException {
+ // Netty's CompactObjectOutputStream writes a single version byte instead of the
+ // standard 4-byte Java stream header (ACED 0005)
+ int version = readByte();
+ if (version != 5) {
+ throw new StreamCorruptedException(
+ "Unsupported version: " + version);
+ }
+ }
+
+ @Override
+ protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
+ int type = read();
+ if (type < 0) {
+ throw new EOFException();
+ }
+ switch (type) {
+ case TYPE_FAT_DESCRIPTOR:
+ return super.readClassDescriptor();
+ case TYPE_THIN_DESCRIPTOR:
+ String className = readUTF();
+ Class<?> clazz = classResolver.resolve(className);
+ return ObjectStreamClass.lookupAny(clazz);
+ default:
+ throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
+ }
+ }
+
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ try {
+ return classResolver.resolve(desc.getName());
+ } catch (ClassNotFoundException e) {
+ return super.resolveClass(desc);
+ }
+ }
+ }
}