From 8b4cbfd86783e3f99a6bb29b72dc79eebe75680b Mon Sep 17 00:00:00 2001 From: EvanYao826 <2869018789@qq.com> Date: Sun, 17 May 2026 10:35:51 +0800 Subject: [PATCH] fix: enforce Serializable check in PojoUtils.realize when Map has no "class" key When a generic Map does not carry a "class" entry, the target type comes from the method signature. The existing Serializable check was only applied when a "class" key was present, allowing non-Serializable DTOs to slip through when the key was absent. Add a check that rejects non-Serializable types even when the Map has no "class" key, consistent with the behavior when the key is present. Fixes #16270 --- *AI-agent involvement: This contribution was assisted by an AI coding agent.* Signed-off-by: EvanYao826 <2869018789@qq.com> --- .../apache/dubbo/common/utils/PojoUtils.java | 18 ++++++++ .../dubbo/common/utils/PojoUtilsTest.java | 42 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index e861c3f2987d..2aecbea19f54 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.rpc.model.ApplicationModel; +import java.io.Serializable; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -61,6 +62,7 @@ import java.util.function.Supplier; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_REFLECTIVE_OPERATION_FAILED; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNTRUSTED_SERIALIZE_CLASS; import static org.apache.dubbo.common.utils.ClassUtils.isAssignableFrom; /** @@ -464,6 +466,22 @@ private static Object realize1( } } + // When the Map does not carry a "class" key, the type comes from the + // method signature. We still need to enforce the Serializable contract + // so that non-Serializable DTOs are rejected consistently (they are + // already rejected when a "class" key is present, via + // DefaultSerializeClassChecker.loadClass). + if (!(className instanceof String) && !type.isPrimitive() && !Serializable.class.isAssignableFrom(type)) { + DefaultSerializeClassChecker checker = DefaultSerializeClassChecker.getInstance(); + String msg = "[Serialization Security] Serialized class " + type.getName() + + " has not implement Serializable interface. " + + "Current mode is strict check, will disallow to deserialize it by default. "; + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); + if (checker.isCheckSerializable()) { + throw new IllegalArgumentException(msg); + } + } + // special logic for enum if (type.isEnum()) { Object name = ((Map) pojo).get("name"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 5cb48a25a77c..834de8dda494 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -1286,4 +1286,46 @@ interface Message { boolean isUrgent(); } + + /** + * Non-Serializable POJO used to verify that PojoUtils.realize enforces + * the Serializable contract even when the generic Map has no "class" key. + * See issue #16270. + */ + public static class NonSerializableDto { + private String label; + private Integer sequence; + + public NonSerializableDto() {} + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Integer getSequence() { + return sequence; + } + + public void setSequence(Integer sequence) { + this.sequence = sequence; + } + } + + @Test + void test_realize_rejectsNonSerializableMapWithoutClassKey() { + // A generic Map that does NOT carry a "class" entry. + Map data = new LinkedHashMap<>(); + data.put("label", "no-class-key"); + data.put("sequence", 100); + + // PojoUtils.realize should reject the non-Serializable target type + // even when the Map has no "class" key (issue #16270). + Assertions.assertThrows(IllegalArgumentException.class, () -> { + PojoUtils.realize(data, NonSerializableDto.class, NonSerializableDto.class); + }); + } }