This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch ci-issue-CAMEL-23324 in repository https://gitbox.apache.org/repos/asf/camel.git
commit bded00c9734168a7d60c9c3692bcb23fc4353a9f Author: Andrea Cosentino <[email protected]> AuthorDate: Tue Apr 14 18:38:27 2026 +0200 CAMEL-23324: Add deserialization filtering for camel-vertx-http and camel-netty-http helper utilities Aligns the HTTP transports with the defense-in-depth pattern applied in camel-netty (CAMEL-23297), camel-mina (CAMEL-23319) and the JMS family (CAMEL-23321). NettyHttpHelper.deserializeJavaObjectFromStream and VertxHttpHelper.deserializeJavaObjectFromStream now apply an ObjectInputFilter to the ObjectInputStream used when reading application/x-java-serialized-object payloads. Filter resolution follows the same priority as the other components: configured pattern -> JVM -Djdk.serialFilter -> default "java.**;javax.**;org.apache.camel.**;!*". The configured pattern is exposed as a new endpoint option 'deserializationFilter' on NettyHttpConfiguration (camel-netty-http) and VertxHttpConfiguration (camel-vertx-http), passed through from NettyHttpProducer and DefaultVertxHttpBinding respectively. Signed-off-by: Andrea Cosentino <[email protected]> --- .../http/NettyHttpConfigurationConfigurer.java | 6 ++ .../netty/http/NettyHttpEndpointConfigurer.java | 6 ++ .../netty/http/NettyHttpEndpointUriFactory.java | 3 +- .../camel/component/netty/http/netty-http.json | 29 ++++----- .../netty/http/NettyHttpConfiguration.java | 17 ++++++ .../component/netty/http/NettyHttpHelper.java | 41 ++++++++++++- .../component/netty/http/NettyHttpProducer.java | 3 +- .../example/external/NotAllowedSerializable.java | 37 +++++++++++ .../NettyHttpHelperDeserializationFilterTest.java | 71 ++++++++++++++++++++++ .../vertx/http/VertxHttpEndpointConfigurer.java | 6 ++ .../vertx/http/VertxHttpEndpointUriFactory.java | 3 +- .../camel/component/vertx/http/vertx-http.json | 3 +- .../vertx/http/DefaultVertxHttpBinding.java | 3 +- .../vertx/http/VertxHttpConfiguration.java | 17 ++++++ .../component/vertx/http/VertxHttpHelper.java | 38 ++++++++++++ .../example/external/NotAllowedSerializable.java | 37 +++++++++++ .../VertxHttpHelperDeserializationFilterTest.java | 71 ++++++++++++++++++++++ 17 files changed, 371 insertions(+), 20 deletions(-) diff --git a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpConfigurationConfigurer.java b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpConfigurationConfigurer.java index 089db6789253..1bbc025ed288 100644 --- a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpConfigurationConfigurer.java +++ b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpConfigurationConfigurer.java @@ -56,6 +56,8 @@ public class NettyHttpConfigurationConfigurer extends org.apache.camel.support.c case "decodersaslist": case "decodersAsList": target.setDecodersAsList(property(camelContext, java.util.List.class, value)); return true; case "delimiter": target.setDelimiter(property(camelContext, org.apache.camel.component.netty.TextLineDelimiter.class, value)); return true; + case "deserializationfilter": + case "deserializationFilter": target.setDeserializationFilter(property(camelContext, java.lang.String.class, value)); return true; case "disablestreamcache": case "disableStreamCache": target.setDisableStreamCache(property(camelContext, boolean.class, value)); return true; case "disconnect": target.setDisconnect(property(camelContext, boolean.class, value)); return true; @@ -228,6 +230,8 @@ public class NettyHttpConfigurationConfigurer extends org.apache.camel.support.c case "decodersaslist": case "decodersAsList": return java.util.List.class; case "delimiter": return org.apache.camel.component.netty.TextLineDelimiter.class; + case "deserializationfilter": + case "deserializationFilter": return java.lang.String.class; case "disablestreamcache": case "disableStreamCache": return boolean.class; case "disconnect": return boolean.class; @@ -401,6 +405,8 @@ public class NettyHttpConfigurationConfigurer extends org.apache.camel.support.c case "decodersaslist": case "decodersAsList": return target.getDecodersAsList(); case "delimiter": return target.getDelimiter(); + case "deserializationfilter": + case "deserializationFilter": return target.getDeserializationFilter(); case "disablestreamcache": case "disableStreamCache": return target.isDisableStreamCache(); case "disconnect": return target.isDisconnect(); diff --git a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointConfigurer.java b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointConfigurer.java index a92498d8a335..ad6675151d46 100644 --- a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointConfigurer.java +++ b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointConfigurer.java @@ -31,6 +31,8 @@ public class NettyHttpEndpointConfigurer extends NettyEndpointConfigurer impleme case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.netty.http.NettyHttpConfiguration.class, value)); return true; case "cookiehandler": case "cookieHandler": target.setCookieHandler(property(camelContext, org.apache.camel.http.base.cookie.CookieHandler.class, value)); return true; + case "deserializationfilter": + case "deserializationFilter": target.getConfiguration().setDeserializationFilter(property(camelContext, java.lang.String.class, value)); return true; case "disablestreamcache": case "disableStreamCache": target.getConfiguration().setDisableStreamCache(property(camelContext, boolean.class, value)); return true; case "headerfilterstrategy": @@ -88,6 +90,8 @@ public class NettyHttpEndpointConfigurer extends NettyEndpointConfigurer impleme case "configuration": return org.apache.camel.component.netty.http.NettyHttpConfiguration.class; case "cookiehandler": case "cookieHandler": return org.apache.camel.http.base.cookie.CookieHandler.class; + case "deserializationfilter": + case "deserializationFilter": return java.lang.String.class; case "disablestreamcache": case "disableStreamCache": return boolean.class; case "headerfilterstrategy": @@ -146,6 +150,8 @@ public class NettyHttpEndpointConfigurer extends NettyEndpointConfigurer impleme case "configuration": return target.getConfiguration(); case "cookiehandler": case "cookieHandler": return target.getCookieHandler(); + case "deserializationfilter": + case "deserializationFilter": return target.getConfiguration().getDeserializationFilter(); case "disablestreamcache": case "disableStreamCache": return target.getConfiguration().isDisableStreamCache(); case "headerfilterstrategy": diff --git a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointUriFactory.java b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointUriFactory.java index a339d7b0f087..b39937136cd2 100644 --- a/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointUriFactory.java +++ b/components/camel-netty-http/src/generated/java/org/apache/camel/component/netty/http/NettyHttpEndpointUriFactory.java @@ -23,7 +23,7 @@ public class NettyHttpEndpointUriFactory extends org.apache.camel.support.compon private static final Set<String> SECRET_PROPERTY_NAMES; private static final Map<String, String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(88); + Set<String> props = new HashSet<>(89); props.add("allowSerializedHeaders"); props.add("backlog"); props.add("bossCount"); @@ -38,6 +38,7 @@ public class NettyHttpEndpointUriFactory extends org.apache.camel.support.compon props.add("connectTimeout"); props.add("cookieHandler"); props.add("decoders"); + props.add("deserializationFilter"); props.add("disableStreamCache"); props.add("disconnect"); props.add("disconnectOnNoReply"); diff --git a/components/camel-netty-http/src/generated/resources/META-INF/org/apache/camel/component/netty/http/netty-http.json b/components/camel-netty-http/src/generated/resources/META-INF/org/apache/camel/component/netty/http/netty-http.json index 242564aaa62f..505ffc6f5948 100644 --- a/components/camel-netty-http/src/generated/resources/META-INF/org/apache/camel/component/netty/http/netty-http.json +++ b/components/camel-netty-http/src/generated/resources/META-INF/org/apache/camel/component/netty/http/netty-http.json @@ -206,19 +206,20 @@ "workerGroup": { "index": 71, "kind": "parameter", "displayName": "Worker Group", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "io.netty.channel.EventLoopGroup", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "To use a explicit EventLoopGroup as the boss thread pool. For example to share a [...] "decoders": { "index": 72, "kind": "parameter", "displayName": "Decoders", "group": "codec", "label": "codec", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "A list of decoders to be used. You can use a String which have values separated by comma, and have the values [...] "encoders": { "index": 73, "kind": "parameter", "displayName": "Encoders", "group": "codec", "label": "codec", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "A list of encoders to be used. You can use a String which have values separated by comma, and have the values [...] - "enabledProtocols": { "index": 74, "kind": "parameter", "displayName": "Enabled Protocols", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TLSv1.2,TLSv1.3", "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Which protocols to enable when using SSL" }, - "hostnameVerification": { "index": 75, "kind": "parameter", "displayName": "Hostname Verification", "group": "security", "label": "common,security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "To enable\/disable hostname verification on SSLEngine" }, - "keyStoreFormat": { "index": 76, "kind": "parameter", "displayName": "Key Store Format", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Keystore format to be used for payload encryption. Defaults to JKS if not set" }, - "keyStoreResource": { "index": 77, "kind": "parameter", "displayName": "Key Store Resource", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Client side certificate keystore [...] - "needClientAuth": { "index": 78, "kind": "parameter", "displayName": "Need Client Auth", "group": "security", "label": "consumer,security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Configures whether the server needs client authentication when us [...] - "passphrase": { "index": 79, "kind": "parameter", "displayName": "Passphrase", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Password to use for the keyStore and trustStore. The same password must be configured for both resou [...] - "securityConfiguration": { "index": 80, "kind": "parameter", "displayName": "Security Configuration", "group": "security", "label": "consumer,security", "required": false, "type": "object", "javaType": "org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to a org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration for configuring secure web resources." }, - "securityOptions": { "index": 81, "kind": "parameter", "displayName": "Security Options", "group": "security", "label": "consumer,security", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "securityConfiguration.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure NettyHttpSecurityConfiguration using key\/value pairs from the map. This is a multi-value option with [...] - "securityProvider": { "index": 82, "kind": "parameter", "displayName": "Security Provider", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Security provider to be used for payload encryption. Defaults to SunX509 if not set." }, - "ssl": { "index": 83, "kind": "parameter", "displayName": "Ssl", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Setting to specify whether SSL encryption is applied to this endpoint" }, - "sslClientCertHeaders": { "index": 84, "kind": "parameter", "displayName": "Ssl Client Cert Headers", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "When enabled and in SSL mode, then the Netty consumer will en [...] - "sslContextParameters": { "index": 85, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "To configure security using SSLContextParam [...] - "sslHandler": { "index": 86, "kind": "parameter", "displayName": "Ssl Handler", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "io.netty.handler.ssl.SslHandler", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Reference to a class that could be used to return an SSL Handler" }, - "trustStoreResource": { "index": 87, "kind": "parameter", "displayName": "Trust Store Resource", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Server side certificate keys [...] + "deserializationFilter": { "index": 74, "kind": "parameter", "displayName": "Deserialization Filter", "group": "security", "label": "producer,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied [...] + "enabledProtocols": { "index": 75, "kind": "parameter", "displayName": "Enabled Protocols", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TLSv1.2,TLSv1.3", "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Which protocols to enable when using SSL" }, + "hostnameVerification": { "index": 76, "kind": "parameter", "displayName": "Hostname Verification", "group": "security", "label": "common,security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "To enable\/disable hostname verification on SSLEngine" }, + "keyStoreFormat": { "index": 77, "kind": "parameter", "displayName": "Key Store Format", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Keystore format to be used for payload encryption. Defaults to JKS if not set" }, + "keyStoreResource": { "index": 78, "kind": "parameter", "displayName": "Key Store Resource", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Client side certificate keystore [...] + "needClientAuth": { "index": 79, "kind": "parameter", "displayName": "Need Client Auth", "group": "security", "label": "consumer,security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Configures whether the server needs client authentication when us [...] + "passphrase": { "index": 80, "kind": "parameter", "displayName": "Passphrase", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Password to use for the keyStore and trustStore. The same password must be configured for both resou [...] + "securityConfiguration": { "index": 81, "kind": "parameter", "displayName": "Security Configuration", "group": "security", "label": "consumer,security", "required": false, "type": "object", "javaType": "org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "Refers to a org.apache.camel.component.netty.http.NettyHttpSecurityConfiguration for configuring secure web resources." }, + "securityOptions": { "index": 82, "kind": "parameter", "displayName": "Security Options", "group": "security", "label": "consumer,security", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "securityConfiguration.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure NettyHttpSecurityConfiguration using key\/value pairs from the map. This is a multi-value option with [...] + "securityProvider": { "index": 83, "kind": "parameter", "displayName": "Security Provider", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Security provider to be used for payload encryption. Defaults to SunX509 if not set." }, + "ssl": { "index": 84, "kind": "parameter", "displayName": "Ssl", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Setting to specify whether SSL encryption is applied to this endpoint" }, + "sslClientCertHeaders": { "index": 85, "kind": "parameter", "displayName": "Ssl Client Cert Headers", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "When enabled and in SSL mode, then the Netty consumer will en [...] + "sslContextParameters": { "index": 86, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "To configure security using SSLContextParam [...] + "sslHandler": { "index": 87, "kind": "parameter", "displayName": "Ssl Handler", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "io.netty.handler.ssl.SslHandler", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Reference to a class that could be used to return an SSL Handler" }, + "trustStoreResource": { "index": 88, "kind": "parameter", "displayName": "Trust Store Resource", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.netty.http.NettyHttpConfiguration", "configurationField": "configuration", "description": "Server side certificate keys [...] } } diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java index 9dc4b31e59a9..27ee3820e42b 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpConfiguration.java @@ -53,6 +53,8 @@ public class NettyHttpConfiguration extends NettyConfiguration { private boolean throwExceptionOnFailure = true; @UriParam(label = "advanced") private boolean transferException; + @UriParam(label = "producer,security") + private String deserializationFilter; @UriParam(label = "consumer") private boolean muteException; @UriParam(label = "consumer") @@ -188,6 +190,21 @@ public class NettyHttpConfiguration extends NettyConfiguration { this.transferException = transferException; } + public String getDeserializationFilter() { + return deserializationFilter; + } + + /** + * Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when deserializing Java objects from HTTP + * responses with Content-Type application/x-java-serialized-object. This is used when transferException is enabled + * and the remote side returns a serialized exception. When not set, the filter configured via the JVM system + * property jdk.serialFilter is used when present; otherwise a conservative default filter allowing java., javax. + * and org.apache.camel. packages is applied. + */ + public void setDeserializationFilter(String deserializationFilter) { + this.deserializationFilter = deserializationFilter; + } + public boolean isMuteException() { return muteException; } diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java index 2a13e6441911..6bc10e574f0c 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpHelper.java @@ -18,6 +18,7 @@ package org.apache.camel.component.netty.http; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.net.URI; import java.net.URISyntaxException; @@ -33,12 +34,25 @@ import org.apache.camel.util.IOHelper; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.URISupport; import org.apache.camel.util.UnsafeUriCharactersEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Helpers. */ public final class NettyHttpHelper { + /** + * Default {@link ObjectInputFilter} pattern applied when deserializing Java objects from HTTP responses with + * Content-Type {@code application/x-java-serialized-object}. Allows standard Java types and Apache Camel types and + * rejects everything else. Can be overridden per-endpoint via + * {@link NettyHttpConfiguration#setDeserializationFilter(String)} or globally via the JVM system property + * {@code jdk.serialFilter}. + */ + static final String DEFAULT_DESERIALIZATION_FILTER = "java.**;javax.**;org.apache.camel.**;!*"; + + private static final Logger LOG = LoggerFactory.getLogger(NettyHttpHelper.class); + private NettyHttpHelper() { } @@ -86,6 +100,12 @@ public final class NettyHttpHelper { public static Exception populateNettyHttpOperationFailedException( Exchange exchange, String url, FullHttpResponse response, int responseCode, boolean transferException) { + return populateNettyHttpOperationFailedException(exchange, url, response, responseCode, transferException, null); + } + + public static Exception populateNettyHttpOperationFailedException( + Exchange exchange, String url, FullHttpResponse response, int responseCode, boolean transferException, + String deserializationFilter) { String statusText = response.status().reasonPhrase(); if (responseCode >= 300 && responseCode < 400) { @@ -105,7 +125,7 @@ public final class NettyHttpHelper { InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, response); if (is != null) { try { - Object body = deserializeJavaObjectFromStream(is); + Object body = deserializeJavaObjectFromStream(is, deserializationFilter); if (body instanceof Exception exception) { return exception; } @@ -123,12 +143,18 @@ public final class NettyHttpHelper { } public static Object deserializeJavaObjectFromStream(InputStream is) throws ClassNotFoundException, IOException { + return deserializeJavaObjectFromStream(is, null); + } + + public static Object deserializeJavaObjectFromStream(InputStream is, String deserializationFilter) + throws ClassNotFoundException, IOException { if (is == null) { return null; } Object answer = null; ObjectInputStream ois = new ObjectInputStream(is); + ois.setObjectInputFilter(resolveDeserializationFilter(deserializationFilter)); try { answer = ois.readObject(); } finally { @@ -138,6 +164,19 @@ public final class NettyHttpHelper { return answer; } + private static ObjectInputFilter resolveDeserializationFilter(String configuredPattern) { + if (configuredPattern != null && !configuredPattern.isBlank()) { + return ObjectInputFilter.Config.createFilter(configuredPattern); + } + ObjectInputFilter jvmFilter = ObjectInputFilter.Config.getSerialFilter(); + if (jvmFilter != null) { + return jvmFilter; + } + LOG.debug("No JVM-wide deserialization filter set, applying default Camel filter: {}", + DEFAULT_DESERIALIZATION_FILTER); + return ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER); + } + /** * Creates the URL to invoke. * diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpProducer.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpProducer.java index f3d7e4cefd42..78fbe0609927 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpProducer.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/NettyHttpProducer.java @@ -181,7 +181,8 @@ public class NettyHttpProducer extends NettyProducer { } else if (getConfiguration().isThrowExceptionOnFailure()) { // operation failed so populate exception to throw Exception cause = NettyHttpHelper.populateNettyHttpOperationFailedException(exchange, actualUrl, - response, code, getConfiguration().isTransferException()); + response, code, getConfiguration().isTransferException(), + getConfiguration().getDeserializationFilter()); exchange.setException(cause); } } diff --git a/components/camel-netty-http/src/test/java/com/example/external/NotAllowedSerializable.java b/components/camel-netty-http/src/test/java/com/example/external/NotAllowedSerializable.java new file mode 100644 index 000000000000..204d84216745 --- /dev/null +++ b/components/camel-netty-http/src/test/java/com/example/external/NotAllowedSerializable.java @@ -0,0 +1,37 @@ +/* + * 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 com.example.external; + +import java.io.Serializable; + +/** + * Serializable type living outside the {@code java.**}, {@code javax.**} and {@code org.apache.camel.**} packages, used + * to verify that the default deserialization allowlist rejects unknown classes. + */ +public final class NotAllowedSerializable implements Serializable { + private static final long serialVersionUID = 1L; + + private final String value; + + public NotAllowedSerializable(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java new file mode 100644 index 000000000000..679a9ffb6ea7 --- /dev/null +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpHelperDeserializationFilterTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.netty.http; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InvalidClassException; +import java.io.ObjectOutputStream; + +import com.example.external.NotAllowedSerializable; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class NettyHttpHelperDeserializationFilterTest { + + @Test + public void testDeserializeAllowlistedType() throws Exception { + InputStream is = serialize("hello"); + Object value = NettyHttpHelper.deserializeJavaObjectFromStream(is); + assertInstanceOf(String.class, value); + assertEquals("hello", value); + } + + @Test + public void testDefaultFilterRejectsUnlistedType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("blocked")); + assertThrows(InvalidClassException.class, () -> NettyHttpHelper.deserializeJavaObjectFromStream(is)); + } + + @Test + public void testConfiguredFilterAllowsExternalType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("allowed")); + String filter = "com.example.external.*;java.**;!*"; + Object value = NettyHttpHelper.deserializeJavaObjectFromStream(is, filter); + assertInstanceOf(NotAllowedSerializable.class, value); + assertEquals("allowed", ((NotAllowedSerializable) value).getValue()); + } + + @Test + public void testConfiguredFilterStillRejectsUnlistedType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("blocked")); + String filter = "java.**;!*"; + assertThrows(InvalidClassException.class, () -> NettyHttpHelper.deserializeJavaObjectFromStream(is, filter)); + } + + private static InputStream serialize(Object value) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(value); + } + return new ByteArrayInputStream(baos.toByteArray()); + } +} diff --git a/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointConfigurer.java b/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointConfigurer.java index 028d2a57c19d..1d3f74affaf8 100644 --- a/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointConfigurer.java +++ b/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointConfigurer.java @@ -35,6 +35,8 @@ public class VertxHttpEndpointConfigurer extends PropertyConfigurerSupport imple case "connectTimeout": target.getConfiguration().setConnectTimeout(property(camelContext, int.class, value)); return true; case "cookiestore": case "cookieStore": target.getConfiguration().setCookieStore(property(camelContext, io.vertx.ext.web.client.spi.CookieStore.class, value)); return true; + case "deserializationfilter": + case "deserializationFilter": target.getConfiguration().setDeserializationFilter(property(camelContext, java.lang.String.class, value)); return true; case "headerfilterstrategy": case "headerFilterStrategy": target.getConfiguration().setHeaderFilterStrategy(property(camelContext, org.apache.camel.spi.HeaderFilterStrategy.class, value)); return true; case "httpmethod": @@ -95,6 +97,8 @@ public class VertxHttpEndpointConfigurer extends PropertyConfigurerSupport imple case "connectTimeout": return int.class; case "cookiestore": case "cookieStore": return io.vertx.ext.web.client.spi.CookieStore.class; + case "deserializationfilter": + case "deserializationFilter": return java.lang.String.class; case "headerfilterstrategy": case "headerFilterStrategy": return org.apache.camel.spi.HeaderFilterStrategy.class; case "httpmethod": @@ -156,6 +160,8 @@ public class VertxHttpEndpointConfigurer extends PropertyConfigurerSupport imple case "connectTimeout": return target.getConfiguration().getConnectTimeout(); case "cookiestore": case "cookieStore": return target.getConfiguration().getCookieStore(); + case "deserializationfilter": + case "deserializationFilter": return target.getConfiguration().getDeserializationFilter(); case "headerfilterstrategy": case "headerFilterStrategy": return target.getConfiguration().getHeaderFilterStrategy(); case "httpmethod": diff --git a/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointUriFactory.java b/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointUriFactory.java index 15753f394fb2..1d6daa966593 100644 --- a/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointUriFactory.java +++ b/components/camel-vertx/camel-vertx-http/src/generated/java/org/apache/camel/component/vertx/http/VertxHttpEndpointUriFactory.java @@ -23,13 +23,14 @@ public class VertxHttpEndpointUriFactory extends org.apache.camel.support.compon private static final Set<String> SECRET_PROPERTY_NAMES; private static final Map<String, String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(28); + Set<String> props = new HashSet<>(29); props.add("basicAuthPassword"); props.add("basicAuthUsername"); props.add("bearerToken"); props.add("bridgeEndpoint"); props.add("connectTimeout"); props.add("cookieStore"); + props.add("deserializationFilter"); props.add("headerFilterStrategy"); props.add("httpMethod"); props.add("httpUri"); diff --git a/components/camel-vertx/camel-vertx-http/src/generated/resources/META-INF/org/apache/camel/component/vertx/http/vertx-http.json b/components/camel-vertx/camel-vertx-http/src/generated/resources/META-INF/org/apache/camel/component/vertx/http/vertx-http.json index a4ce887ce5a5..b79b8f35969a 100644 --- a/components/camel-vertx/camel-vertx-http/src/generated/resources/META-INF/org/apache/camel/component/vertx/http/vertx-http.json +++ b/components/camel-vertx/camel-vertx-http/src/generated/resources/META-INF/org/apache/camel/component/vertx/http/vertx-http.json @@ -83,6 +83,7 @@ "basicAuthPassword": { "index": 24, "kind": "parameter", "displayName": "Basic Auth Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "The password to use for basic authentication" }, "basicAuthUsername": { "index": 25, "kind": "parameter", "displayName": "Basic Auth Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "The user name to use for basic authentication" }, "bearerToken": { "index": 26, "kind": "parameter", "displayName": "Bearer Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "The bearer token to use for bearer token authentication" }, - "sslContextParameters": { "index": 27, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "To configure security using SSLContextParam [...] + "deserializationFilter": { "index": 27, "kind": "parameter", "displayName": "Deserialization Filter", "group": "security", "label": "producer,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied [...] + "sslContextParameters": { "index": 28, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.vertx.http.VertxHttpConfiguration", "configurationField": "configuration", "description": "To configure security using SSLContextParam [...] } } diff --git a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/DefaultVertxHttpBinding.java b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/DefaultVertxHttpBinding.java index 020afd8c9de1..8b76a47888ca 100644 --- a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/DefaultVertxHttpBinding.java +++ b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/DefaultVertxHttpBinding.java @@ -224,7 +224,8 @@ public class DefaultVertxHttpBinding implements VertxHttpBinding { = exchange.getContext().getTypeConverter().convertTo(InputStream.class, responseBody.getBytes()); if (inputStream != null) { try { - return VertxHttpHelper.deserializeJavaObjectFromStream(inputStream); + return VertxHttpHelper.deserializeJavaObjectFromStream(inputStream, + endpoint.getConfiguration().getDeserializationFilter()); } finally { IOHelper.close(inputStream); } diff --git a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpConfiguration.java b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpConfiguration.java index 2eddb73570d3..2f8ddff8c6b7 100644 --- a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpConfiguration.java +++ b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpConfiguration.java @@ -52,6 +52,8 @@ public class VertxHttpConfiguration { private boolean throwExceptionOnFailure = true; @UriParam(label = "producer", defaultValue = "false") private boolean transferException; + @UriParam(label = "producer,security") + private String deserializationFilter; @UriParam(label = "producer", defaultValue = "200-299") private String okStatusCodeRange = "200-299"; @UriParam(label = "producer", defaultValue = "false") @@ -200,6 +202,21 @@ public class VertxHttpConfiguration { return transferException; } + public String getDeserializationFilter() { + return deserializationFilter; + } + + /** + * Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when deserializing Java objects from HTTP + * responses with Content-Type application/x-java-serialized-object. This is used when transferException is enabled + * (or when allowJavaSerializedObject is enabled on the component) and the remote side returns a serialized payload. + * When not set, the filter configured via the JVM system property jdk.serialFilter is used when present; otherwise + * a conservative default filter allowing java., javax. and org.apache.camel. packages is applied. + */ + public void setDeserializationFilter(String deserializationFilter) { + this.deserializationFilter = deserializationFilter; + } + /** * The status codes which are considered a success response. The values are inclusive. Multiple ranges can be * defined, separated by comma, e.g. 200-204,209,301-304. Each range must be a single number or from-to with the diff --git a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java index c377f3125bbf..05184d5756a2 100644 --- a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java +++ b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpHelper.java @@ -18,6 +18,7 @@ package org.apache.camel.component.vertx.http; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -30,9 +31,22 @@ import org.apache.camel.Message; import org.apache.camel.util.IOHelper; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.UnsafeUriCharactersEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class VertxHttpHelper { + /** + * Default {@link ObjectInputFilter} pattern applied when deserializing Java objects from HTTP responses with + * Content-Type {@code application/x-java-serialized-object}. Allows standard Java types and Apache Camel types and + * rejects everything else. Can be overridden per-endpoint via + * {@link VertxHttpConfiguration#setDeserializationFilter(String)} or globally via the JVM system property + * {@code jdk.serialFilter}. + */ + static final String DEFAULT_DESERIALIZATION_FILTER = "java.**;javax.**;org.apache.camel.**;!*"; + + private static final Logger LOG = LoggerFactory.getLogger(VertxHttpHelper.class); + private VertxHttpHelper() { // Utility class } @@ -118,12 +132,23 @@ public final class VertxHttpHelper { * Deserializes an object from the given {@link InputStream} */ public static Object deserializeJavaObjectFromStream(InputStream is) throws ClassNotFoundException, IOException { + return deserializeJavaObjectFromStream(is, null); + } + + /** + * Deserializes an object from the given {@link InputStream} applying an {@link ObjectInputFilter} resolved from the + * supplied pattern, the JVM-wide {@code jdk.serialFilter} or the Camel default + * ({@value #DEFAULT_DESERIALIZATION_FILTER}) in that order. + */ + public static Object deserializeJavaObjectFromStream(InputStream is, String deserializationFilter) + throws ClassNotFoundException, IOException { if (is == null) { return null; } Object answer; ObjectInputStream ois = new ObjectInputStream(is); + ois.setObjectInputFilter(resolveDeserializationFilter(deserializationFilter)); try { answer = ois.readObject(); } finally { @@ -133,6 +158,19 @@ public final class VertxHttpHelper { return answer; } + private static ObjectInputFilter resolveDeserializationFilter(String configuredPattern) { + if (configuredPattern != null && !configuredPattern.isBlank()) { + return ObjectInputFilter.Config.createFilter(configuredPattern); + } + ObjectInputFilter jvmFilter = ObjectInputFilter.Config.getSerialFilter(); + if (jvmFilter != null) { + return jvmFilter; + } + LOG.debug("No JVM-wide deserialization filter set, applying default Camel filter: {}", + DEFAULT_DESERIALIZATION_FILTER); + return ObjectInputFilter.Config.createFilter(DEFAULT_DESERIALIZATION_FILTER); + } + /** * Retrieves the charset from the exchange Content-Type header, or falls back to the CamelCharsetName exchange * property when not available diff --git a/components/camel-vertx/camel-vertx-http/src/test/java/com/example/external/NotAllowedSerializable.java b/components/camel-vertx/camel-vertx-http/src/test/java/com/example/external/NotAllowedSerializable.java new file mode 100644 index 000000000000..204d84216745 --- /dev/null +++ b/components/camel-vertx/camel-vertx-http/src/test/java/com/example/external/NotAllowedSerializable.java @@ -0,0 +1,37 @@ +/* + * 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 com.example.external; + +import java.io.Serializable; + +/** + * Serializable type living outside the {@code java.**}, {@code javax.**} and {@code org.apache.camel.**} packages, used + * to verify that the default deserialization allowlist rejects unknown classes. + */ +public final class NotAllowedSerializable implements Serializable { + private static final long serialVersionUID = 1L; + + private final String value; + + public NotAllowedSerializable(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java new file mode 100644 index 000000000000..4d4d49f88618 --- /dev/null +++ b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpHelperDeserializationFilterTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.vertx.http; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InvalidClassException; +import java.io.ObjectOutputStream; + +import com.example.external.NotAllowedSerializable; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class VertxHttpHelperDeserializationFilterTest { + + @Test + public void testDeserializeAllowlistedType() throws Exception { + InputStream is = serialize("hello"); + Object value = VertxHttpHelper.deserializeJavaObjectFromStream(is); + assertInstanceOf(String.class, value); + assertEquals("hello", value); + } + + @Test + public void testDefaultFilterRejectsUnlistedType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("blocked")); + assertThrows(InvalidClassException.class, () -> VertxHttpHelper.deserializeJavaObjectFromStream(is)); + } + + @Test + public void testConfiguredFilterAllowsExternalType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("allowed")); + String filter = "com.example.external.*;java.**;!*"; + Object value = VertxHttpHelper.deserializeJavaObjectFromStream(is, filter); + assertInstanceOf(NotAllowedSerializable.class, value); + assertEquals("allowed", ((NotAllowedSerializable) value).getValue()); + } + + @Test + public void testConfiguredFilterStillRejectsUnlistedType() throws Exception { + InputStream is = serialize(new NotAllowedSerializable("blocked")); + String filter = "java.**;!*"; + assertThrows(InvalidClassException.class, () -> VertxHttpHelper.deserializeJavaObjectFromStream(is, filter)); + } + + private static InputStream serialize(Object value) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(value); + } + return new ByteArrayInputStream(baos.toByteArray()); + } +}
