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

martinweiler pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git


The following commit(s) were added to refs/heads/main by this push:
     new 7f283f3d76c [incubator-kie#7253] Use the Drools deserialization class 
allowlist for Java-serialized process variables (#7254)
7f283f3d76c is described below

commit 7f283f3d76c2fd402a4f441f946ba1271a70cc0f
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Nov 5 18:27:34 2026 +0100

    [incubator-kie#7253] Use the Drools deserialization class allowlist for 
Java-serialized process variables (#7254)
    
    * [incubator-kie#7253] Use the Drools deserialization class allowlist for 
Java-serialized process variables
    
    JavaSerializationUtils.deserialize, used by ProtobufObjectMarshallerStrategy
    and SerializableProtostreamBaseMarshaller, now sets an ObjectInputFilter
    when drools.serialization.enableDeserializationFilter is true (the
    default). It accepts the classes DeserializationFilterHelper allows
    (java.lang, java.util, java.math, java.time and the patterns in
    drools.serialization.allowedDeserializationClassPatterns), the java.sql
    date and time types, ProcessWorkItemHandlerException (stored by
    WorkItemNodeInstance in the Error variable of an error handling
    process), primitives and arrays of those, and keeps a JVM-wide
    jdk.serialFilter in force.
    
    Application classes stored in process variables through Java
    serialization have to be added to
    drools.serialization.allowedDeserializationClassPatterns; jbpm-tests
    does so for its test model classes.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    * [incubator-kie#7253] Allow the MongoDB persistence test model classes in 
the deserialization filter
    
    MongoDBProcessInstancesIT keeps Address and PersonWithAddresses in
    process variables through Java serialization. Add both classes to
    drools.serialization.allowedDeserializationClassPatterns in the
    module's failsafe configuration, as jbpm-tests does for its test model
    classes.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 kogito-addons/common/persistence/mongodb/pom.xml   |   2 +
 kogito-jbpm/jbpm-tests/pom.xml                     |   2 +
 kogito-jbpm/process-serialization-protobuf/pom.xml |   4 +
 .../flow/serialization/JavaSerializationUtils.java |  62 ++++++++++
 .../DeserializationFilterTestSupport.java          |  61 +++++++++
 .../serialization/JavaSerializationUtilsTest.java  | 137 +++++++++++++++++++++
 .../serialization/ProcessInstanceMarshallTest.java |  11 ++
 .../ProtobufObjectMarshallerStrategyTest.java      |  79 ++++++++++++
 .../SerializableProtostreamBaseMarshallerTest.java |  83 +++++++++++++
 9 files changed, 441 insertions(+)

diff --git a/kogito-addons/common/persistence/mongodb/pom.xml 
b/kogito-addons/common/persistence/mongodb/pom.xml
index 86ac3571937..407a950406f 100644
--- a/kogito-addons/common/persistence/mongodb/pom.xml
+++ b/kogito-addons/common/persistence/mongodb/pom.xml
@@ -132,6 +132,8 @@
         <configuration>
           <systemPropertyVariables combine.children="append">
               
<container.image.mongodb>${container.image.mongodb}</container.image.mongodb>
+              <!-- test model classes kept in process variables through Java 
serialization -->
+              
<drools.serialization.allowedDeserializationClassPatterns>org.kie.kogito.mongodb.Address;org.kie.kogito.mongodb.PersonWithAddresses</drools.serialization.allowedDeserializationClassPatterns>
           </systemPropertyVariables>
         </configuration>
         <executions>
diff --git a/kogito-jbpm/jbpm-tests/pom.xml b/kogito-jbpm/jbpm-tests/pom.xml
index 5c91d7073a1..9547a817cf1 100644
--- a/kogito-jbpm/jbpm-tests/pom.xml
+++ b/kogito-jbpm/jbpm-tests/pom.xml
@@ -172,6 +172,8 @@
             <!-- enforce strict variable checking for the sake of
             tests -->
             <org.jbpm.variable.strict>true</org.jbpm.variable.strict>
+            <!-- test model classes kept in process variables through Java 
serialization -->
+            
<drools.serialization.allowedDeserializationClassPatterns>org.jbpm.bpmn2.objects.*</drools.serialization.allowedDeserializationClassPatterns>
           </systemPropertyVariables>
         </configuration>
       </plugin>
diff --git a/kogito-jbpm/process-serialization-protobuf/pom.xml 
b/kogito-jbpm/process-serialization-protobuf/pom.xml
index 19b26ada89d..c763ca8cecd 100644
--- a/kogito-jbpm/process-serialization-protobuf/pom.xml
+++ b/kogito-jbpm/process-serialization-protobuf/pom.xml
@@ -44,6 +44,10 @@
       <groupId>org.kie.kogito</groupId>
       <artifactId>jbpm-flow</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.drools</groupId>
+      <artifactId>drools-core</artifactId>
+    </dependency>
 
     <dependency>
         <groupId>org.kie.kogito</groupId>
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
index fddfcc20b8f..e278055c0a9 100644
--- 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
@@ -22,12 +22,34 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.ObjectInputFilter;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
+import java.util.Set;
+
+import org.drools.core.util.DeserializationFilterHelper;
+import org.drools.core.util.KeyStoreConstants;
+import org.kie.api.runtime.process.ProcessWorkItemHandlerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public final class JavaSerializationUtils {
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(JavaSerializationUtils.class);
+
+    /**
+     * Process variable types outside the packages {@link 
DeserializationFilterHelper} allows by default:
+     * the JDBC date and time types, and the exception that {@code 
WorkItemNodeInstance} stores in the
+     * {@code Error} variable of a work item error handling process.
+     */
+    private static final Set<String> ALLOWED_CLASSES = Set.of(
+            java.sql.Date.class.getName(),
+            java.sql.Time.class.getName(),
+            java.sql.Timestamp.class.getName(),
+            ProcessWorkItemHandlerException.class.getName(),
+            ProcessWorkItemHandlerException.HandlingStrategy.class.getName());
+
     private JavaSerializationUtils() {
     }
 
@@ -50,6 +72,16 @@ public final class JavaSerializationUtils {
      * Deserializes {@code data} using Java serialization, resolving classes 
through the thread
      * context classloader first so that application classes are found in 
environments like Quarkus
      * where the bootstrap classloader does not see deployment classes.
+     * <p>
+     * The classes that can be read are limited by an {@link 
ObjectInputFilter} that shares its
+     * allowlist with Drools session marshalling ({@link 
DeserializationFilterHelper}): the
+     * {@code java.lang}, {@code java.util}, {@code java.math} and {@code 
java.time} packages, the
+     * {@code java.sql} date and time types, {@link 
ProcessWorkItemHandlerException}, and the patterns
+     * listed in the {@value 
KeyStoreConstants#PROP_ALLOWED_DESER_CLASS_PATTERNS} system property. 
Application
+     * classes stored in process variables through Java serialization must be 
added to that
+     * property, for example {@code 
-Ddrools.serialization.allowedDeserializationClassPatterns=com.example.model.*}.
+     * The filter can be turned off with {@code -D}{@value 
KeyStoreConstants#PROP_ENABLE_DESER_FILTER}{@code =false}.
+     * A JVM-wide filter set with {@code jdk.serialFilter} keeps applying as 
well.
      */
     public static Object deserialize(byte[] data) throws IOException, 
ClassNotFoundException {
         try (InputStream is = new ByteArrayInputStream(data); 
ObjectInputStream ois = new ObjectInputStream(is) {
@@ -62,7 +94,37 @@ public final class JavaSerializationUtils {
                 }
             }
         }) {
+            if (DeserializationFilterHelper.isDeserializationFilterEnabled()) {
+                ois.setObjectInputFilter(createDeserializationFilter());
+            }
             return ois.readObject();
         }
     }
+
+    private static ObjectInputFilter createDeserializationFilter() {
+        ObjectInputFilter filter = JavaSerializationUtils::checkInput;
+        ObjectInputFilter jvmWideFilter = 
ObjectInputFilter.Config.getSerialFilter();
+        return jvmWideFilter == null ? filter : 
ObjectInputFilter.merge(filter, jvmWideFilter);
+    }
+
+    private static ObjectInputFilter.Status 
checkInput(ObjectInputFilter.FilterInfo filterInfo) {
+        Class<?> clazz = filterInfo.serialClass();
+        if (clazz == null) {
+            return ObjectInputFilter.Status.UNDECIDED;
+        }
+        while (clazz.isArray()) {
+            clazz = clazz.getComponentType();
+        }
+        if (clazz.isPrimitive()) {
+            return ObjectInputFilter.Status.ALLOWED;
+        }
+        String className = clazz.getName();
+        if (ALLOWED_CLASSES.contains(className) || 
DeserializationFilterHelper.isClassAllowed(className)) {
+            return ObjectInputFilter.Status.ALLOWED;
+        }
+        LOGGER.warn("Deserialization of class '{}' was rejected by the 
ObjectInputFilter. "
+                + "If this is a legitimate process variable type, add it to 
the allowlist via -D{}=<pattern>",
+                className, 
KeyStoreConstants.PROP_ALLOWED_DESER_CLASS_PATTERNS);
+        return ObjectInputFilter.Status.REJECTED;
+    }
 }
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/DeserializationFilterTestSupport.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/DeserializationFilterTestSupport.java
new file mode 100644
index 00000000000..d86e66c2f73
--- /dev/null
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/DeserializationFilterTestSupport.java
@@ -0,0 +1,61 @@
+/*
+ * 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.jbpm.flow.serialization;
+
+import org.drools.core.util.KeyStoreConstants;
+
+/**
+ * Saves and restores the deserialization filter system properties around a 
test.
+ */
+public class DeserializationFilterTestSupport {
+
+    private String savedAllowedPatterns;
+    private String savedFilterEnabled;
+
+    /**
+     * Saves the current filter properties and resets them to their defaults: 
filter enabled, no extra patterns.
+     */
+    public void setUp() {
+        savedAllowedPatterns = 
System.getProperty(KeyStoreConstants.PROP_ALLOWED_DESER_CLASS_PATTERNS);
+        savedFilterEnabled = 
System.getProperty(KeyStoreConstants.PROP_ENABLE_DESER_FILTER);
+        
System.clearProperty(KeyStoreConstants.PROP_ALLOWED_DESER_CLASS_PATTERNS);
+        System.clearProperty(KeyStoreConstants.PROP_ENABLE_DESER_FILTER);
+    }
+
+    public void allow(String... patterns) {
+        
System.setProperty(KeyStoreConstants.PROP_ALLOWED_DESER_CLASS_PATTERNS, 
String.join(";", patterns));
+    }
+
+    public void disableFilter() {
+        System.setProperty(KeyStoreConstants.PROP_ENABLE_DESER_FILTER, 
"false");
+    }
+
+    public void tearDown() {
+        restore(KeyStoreConstants.PROP_ALLOWED_DESER_CLASS_PATTERNS, 
savedAllowedPatterns);
+        restore(KeyStoreConstants.PROP_ENABLE_DESER_FILTER, 
savedFilterEnabled);
+    }
+
+    private static void restore(String key, String value) {
+        if (value == null) {
+            System.clearProperty(key);
+        } else {
+            System.setProperty(key, value);
+        }
+    }
+}
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
index 7973fcf177d..e3d324c807c 100644
--- 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
@@ -19,11 +19,28 @@
 package org.jbpm.flow.serialization;
 
 import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Stream;
 
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.api.runtime.process.ProcessWorkItemHandlerException;
+import 
org.kie.api.runtime.process.ProcessWorkItemHandlerException.HandlingStrategy;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -60,6 +77,36 @@ public class JavaSerializationUtilsTest {
         // intentionally does not implement Serializable
     }
 
+    /**
+     * Not covered by the default allowlist. Records whether its readObject 
ran.
+     */
+    static class UnlistedValue implements Serializable {
+        private static final long serialVersionUID = 1L;
+        static volatile boolean readObjectInvoked;
+
+        private void readObject(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
+            in.defaultReadObject();
+            readObjectInvoked = true;
+        }
+    }
+
+    static class UnlistedException extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+    }
+
+    private final DeserializationFilterTestSupport filterSupport = new 
DeserializationFilterTestSupport();
+
+    @BeforeEach
+    void setUp() {
+        filterSupport.setUp();
+        UnlistedValue.readObjectInvoked = false;
+    }
+
+    @AfterEach
+    void tearDown() {
+        filterSupport.tearDown();
+    }
+
     // -- tests --------------------------------------------------------------
 
     @Test
@@ -72,6 +119,7 @@ public class JavaSerializationUtilsTest {
 
     @Test
     void roundTripCustomObject() throws Exception {
+        filterSupport.allow(Person.class.getName());
         Person original = new Person("Alice", 30);
         byte[] bytes = JavaSerializationUtils.serialize(original);
         assertThat(bytes).isNotEmpty();
@@ -96,6 +144,7 @@ public class JavaSerializationUtilsTest {
     void serializeBytesAreCompleteAndDeserializable() throws Exception {
         // Regression: toByteArray() called before OOS.close() produced 
truncated bytes
         // causing EOFException on deserialization. Verify bytes are always 
fully flushed.
+        filterSupport.allow(Person.class.getName());
         Person original = new Person("Bob", 42);
         byte[] bytes = JavaSerializationUtils.serialize(original);
         // A valid Java serialization stream starts with the magic bytes 0xACED
@@ -122,6 +171,7 @@ public class JavaSerializationUtilsTest {
     void deserializeUsesThreadContextClassLoader() throws Exception {
         // Verify that deserialize resolves classes through the thread-context 
classloader.
         // We swap in a classloader that can see Person, confirm round-trip 
still works.
+        filterSupport.allow(Person.class.getName());
         ClassLoader original = Thread.currentThread().getContextClassLoader();
         try {
             
Thread.currentThread().setContextClassLoader(Person.class.getClassLoader());
@@ -132,4 +182,91 @@ public class JavaSerializationUtilsTest {
             Thread.currentThread().setContextClassLoader(original);
         }
     }
+
+    // -- deserialization filter ---------------------------------------------
+
+    static Stream<Arguments> jdkValueTypes() {
+        return Stream.of(
+                Arguments.of(BigDecimal.valueOf(10L)),
+                Arguments.of(LocalDate.of(2026, 1, 31)),
+                Arguments.of(new Timestamp(1_000L)),
+                Arguments.of(new java.sql.Date(1_000L)),
+                Arguments.of(new Time(1_000L)),
+                Arguments.of(new HashMap<>(Map.of("key", new 
ArrayList<>(List.of("a", "b"))))),
+                Arguments.of((Object) new int[] { 1, 2, 3 }),
+                Arguments.of((Object) new String[][] { { "a" }, { "b" } }));
+    }
+
+    @ParameterizedTest
+    @MethodSource("jdkValueTypes")
+    void deserializeAllowsJdkValueTypesByDefault(Object original) throws 
Exception {
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(original);
+    }
+
+    @Test
+    void deserializeAllowsWorkItemHandlerExceptionByDefault() throws Exception 
{
+        ProcessWorkItemHandlerException original =
+                new ProcessWorkItemHandlerException("errorHandlingProcess", 
HandlingStrategy.RETRY, new IllegalStateException("handler failed"), 2);
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+
+        Object deserialized = JavaSerializationUtils.deserialize(bytes);
+
+        
assertThat(deserialized).isInstanceOfSatisfying(ProcessWorkItemHandlerException.class,
 exception -> {
+            
assertThat(exception.getProcessId()).isEqualTo("errorHandlingProcess");
+            
assertThat(exception.getStrategy()).isEqualTo(HandlingStrategy.RETRY);
+            assertThat(exception.getRetries()).isEqualTo(2);
+            
assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class).hasMessage("handler
 failed");
+        });
+    }
+
+    @Test
+    void deserializeRejectsWorkItemHandlerExceptionWithCauseNotInAllowlist() 
throws Exception {
+        ProcessWorkItemHandlerException original =
+                new ProcessWorkItemHandlerException("errorHandlingProcess", 
HandlingStrategy.ABORT, new UnlistedException());
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+
+        assertThatThrownBy(() -> JavaSerializationUtils.deserialize(bytes))
+                .isInstanceOf(InvalidClassException.class);
+    }
+
+    @Test
+    void deserializeRejectsClassNotInAllowlist() throws Exception {
+        byte[] bytes = JavaSerializationUtils.serialize(new UnlistedValue());
+        assertThatThrownBy(() -> JavaSerializationUtils.deserialize(bytes))
+                .isInstanceOf(InvalidClassException.class);
+        assertThat(UnlistedValue.readObjectInvoked).isFalse();
+    }
+
+    @Test
+    void deserializeRejectsClassNotInAllowlistInsideAllowedCollection() throws 
Exception {
+        byte[] bytes = JavaSerializationUtils.serialize(new 
ArrayList<>(List.of("value", new UnlistedValue())));
+        assertThatThrownBy(() -> JavaSerializationUtils.deserialize(bytes))
+                .isInstanceOf(InvalidClassException.class);
+        assertThat(UnlistedValue.readObjectInvoked).isFalse();
+    }
+
+    @Test
+    void deserializeRejectsArrayOfClassNotInAllowlist() throws Exception {
+        byte[] bytes = JavaSerializationUtils.serialize(new UnlistedValue[] { 
new UnlistedValue() });
+        assertThatThrownBy(() -> JavaSerializationUtils.deserialize(bytes))
+                .isInstanceOf(InvalidClassException.class);
+        assertThat(UnlistedValue.readObjectInvoked).isFalse();
+    }
+
+    @Test
+    void deserializeAllowsClassMatchingConfiguredPattern() throws Exception {
+        filterSupport.allow("com.example.unrelated.*", 
JavaSerializationUtilsTest.class.getPackageName() + ".*");
+        byte[] bytes = JavaSerializationUtils.serialize(new UnlistedValue());
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isInstanceOf(UnlistedValue.class);
+        assertThat(UnlistedValue.readObjectInvoked).isTrue();
+    }
+
+    @Test
+    void deserializeAcceptsAnyClassWhenFilterIsDisabled() throws Exception {
+        filterSupport.disableFilter();
+        byte[] bytes = JavaSerializationUtils.serialize(new UnlistedValue());
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isInstanceOf(UnlistedValue.class);
+        assertThat(UnlistedValue.readObjectInvoked).isTrue();
+    }
 }
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/ProcessInstanceMarshallTest.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/ProcessInstanceMarshallTest.java
index 431c9982eeb..8c6850a913d 100644
--- 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/ProcessInstanceMarshallTest.java
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/ProcessInstanceMarshallTest.java
@@ -61,6 +61,7 @@ import org.jbpm.workflow.core.impl.WorkflowProcessImpl;
 import org.jbpm.workflow.core.node.EndNode;
 import org.jbpm.workflow.core.node.HumanTaskNode;
 import org.jbpm.workflow.core.node.StartNode;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -92,8 +93,13 @@ public class ProcessInstanceMarshallTest {
 
     private static WorkflowProcessImpl workflow;
 
+    private static final DeserializationFilterTestSupport FILTER_SUPPORT = new 
DeserializationFilterTestSupport();
+
     @BeforeAll
     public static void init() {
+        FILTER_SUPPORT.setUp();
+        FILTER_SUPPORT.allow(MarshableObject.class.getName());
+
         workflow = new WorkflowProcessImpl();
 
         workflow.setId("processId");
@@ -123,6 +129,11 @@ public class ProcessInstanceMarshallTest {
         when(process.get()).thenReturn(workflow);
     }
 
+    @AfterAll
+    public static void cleanUp() {
+        FILTER_SUPPORT.tearDown();
+    }
+
     @XmlRootElement
     public static class MarshableObject implements Serializable {
 
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategyTest.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategyTest.java
new file mode 100644
index 00000000000..9ca0cce44ad
--- /dev/null
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategyTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.jbpm.flow.serialization.impl.marshallers;
+
+import java.io.InvalidClassException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jbpm.flow.serialization.DeserializationFilterTestSupport;
+import org.jbpm.flow.serialization.ProcessInstanceMarshallerException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.protobuf.Any;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class ProtobufObjectMarshallerStrategyTest {
+
+    static class UnlistedValue implements Serializable {
+        private static final long serialVersionUID = 1L;
+    }
+
+    private final DeserializationFilterTestSupport filterSupport = new 
DeserializationFilterTestSupport();
+
+    private final ProtobufObjectMarshallerStrategy strategy = new 
ProtobufObjectMarshallerStrategy();
+
+    @BeforeEach
+    void setUp() {
+        filterSupport.setUp();
+    }
+
+    @AfterEach
+    void tearDown() {
+        filterSupport.tearDown();
+    }
+
+    @Test
+    void unmarshallReturnsAllowedValue() {
+        List<String> original = new ArrayList<>(List.of("a", "b"));
+        Any marshalled = strategy.marshall(original);
+        assertThat(strategy.acceptForUnmarshalling(marshalled)).isTrue();
+        assertThat(strategy.unmarshall(marshalled)).isEqualTo(original);
+    }
+
+    @Test
+    void unmarshallRejectsClassNotInAllowlist() {
+        Any marshalled = strategy.marshall(new UnlistedValue());
+        assertThatThrownBy(() -> strategy.unmarshall(marshalled))
+                .isInstanceOf(ProcessInstanceMarshallerException.class)
+                .hasCauseInstanceOf(InvalidClassException.class);
+    }
+
+    @Test
+    void unmarshallReturnsClassMatchingConfiguredPattern() {
+        filterSupport.allow(UnlistedValue.class.getName());
+        Any marshalled = strategy.marshall(new UnlistedValue());
+        
assertThat(strategy.unmarshall(marshalled)).isInstanceOf(UnlistedValue.class);
+    }
+}
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshallerTest.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshallerTest.java
new file mode 100644
index 00000000000..dc28c1383f4
--- /dev/null
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshallerTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.jbpm.flow.serialization.marshaller;
+
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+import org.infinispan.protostream.MessageMarshaller.ProtoStreamReader;
+import org.jbpm.flow.serialization.DeserializationFilterTestSupport;
+import org.jbpm.flow.serialization.JavaSerializationUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SerializableProtostreamBaseMarshallerTest {
+
+    static class UnlistedValue implements Serializable {
+        private static final long serialVersionUID = 1L;
+    }
+
+    private final DeserializationFilterTestSupport filterSupport = new 
DeserializationFilterTestSupport();
+
+    private final SerializableProtostreamBaseMarshaller marshaller = new 
SerializableProtostreamBaseMarshaller();
+
+    @BeforeEach
+    void setUp() {
+        filterSupport.setUp();
+    }
+
+    @AfterEach
+    void tearDown() {
+        filterSupport.tearDown();
+    }
+
+    @Test
+    void readFromReturnsAllowedValue() throws IOException {
+        ProtoStreamReader reader = readerOf(BigDecimal.TEN);
+        assertThat(marshaller.readFrom(reader)).isEqualTo(BigDecimal.TEN);
+    }
+
+    @Test
+    void readFromRejectsClassNotInAllowlist() throws IOException {
+        ProtoStreamReader reader = readerOf(new UnlistedValue());
+        assertThatThrownBy(() -> marshaller.readFrom(reader))
+                .isInstanceOf(InvalidClassException.class);
+    }
+
+    @Test
+    void readFromReturnsClassMatchingConfiguredPattern() throws IOException {
+        filterSupport.allow(UnlistedValue.class.getName());
+        ProtoStreamReader reader = readerOf(new UnlistedValue());
+        
assertThat(marshaller.readFrom(reader)).isInstanceOf(UnlistedValue.class);
+    }
+
+    private static ProtoStreamReader readerOf(Serializable value) throws 
IOException {
+        ProtoStreamReader reader = mock(ProtoStreamReader.class);
+        
when(reader.readBytes("data")).thenReturn(JavaSerializationUtils.serialize(value));
+        return reader;
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to