This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch backport/22490-to-camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 7b0861c83ffbdb0d7e7f9c449f695b655231ef72
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Apr 8 14:58:31 2026 +0200

    CAMEL-23297: Add deserialization filtering to camel-netty converters and 
codecs (#22490)
    
    - 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.
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../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);
+            }
+        }
+    }
 }

Reply via email to