From 749e91f81564514729ebedbaff282395959b5f36 Mon Sep 17 00:00:00 2001 From: daguimu Date: Fri, 27 Mar 2026 11:52:36 +0800 Subject: [PATCH 1/2] fix(serialize): respect WARN mode in DefaultSerializeClassChecker Fixes #15179 --- .../utils/DefaultSerializeClassChecker.java | 24 ++++++--- .../DefaultSerializeClassCheckerTest.java | 51 ++++++++++++++++++- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java index 029f654abd2..8ac3d6dbdd5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java @@ -111,12 +111,18 @@ public Class loadClass(ClassLoader classLoader, String className) throws Clas String msg = "[Serialization Security] Serialized class " + className + " has not implement Serializable interface. " + "Current mode is strict check, will disallow to deserialize it by default. "; - if (serializeSecurityManager.getWarnedClasses().add(className)) { - logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); + if (checkSerializable && checkStatus != SerializeCheckStatus.WARN + && checkStatus != SerializeCheckStatus.DISABLE) { + if (serializeSecurityManager.getWarnedClasses().add(className)) { + logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); + } + throw new IllegalArgumentException(msg); } - if (checkSerializable) { - throw new IllegalArgumentException(msg); + if (checkStatus == SerializeCheckStatus.WARN) { + if (serializeSecurityManager.getWarnedClasses().add(className)) { + logger.warn(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); + } } } @@ -164,13 +170,14 @@ private Class loadClass0(ClassLoader classLoader, String className) throws Cl if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) { String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " - + "Current mode is `WARN`, will disallow to deserialize it by default. " + + "Current mode is `WARN`, will allow to deserialize it by default. " + + "Dubbo will set to `STRICT` mode by default in the future. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.warn(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } - throw new IllegalArgumentException(msg); + return classForName(classLoader, className); } } @@ -185,13 +192,14 @@ private Class loadClass0(ClassLoader classLoader, String className) throws Cl if (Arrays.binarySearch(disAllowPrefixes, hash) >= 0) { String msg = "[Serialization Security] Serialized class " + className + " is in disallow list. " - + "Current mode is `WARN`, will disallow to deserialize it by default. " + + "Current mode is `WARN`, will allow to deserialize it by default. " + + "Dubbo will set to `STRICT` mode by default in the future. " + "Please add it into security/serialize.allowlist or follow FAQ to configure it."; if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.warn(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); } - throw new IllegalArgumentException(msg); + return classForName(classLoader, className); } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java index 2851b2c050f..adffbb50be9 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java @@ -58,7 +58,8 @@ void testCommon() throws ClassNotFoundException { defaultSerializeClassChecker.loadClass(Thread.currentThread().getContextClassLoader(), int.class.getName()); } - Assertions.assertThrows(IllegalArgumentException.class, () -> { + // In WARN mode, disallowed classes should be loaded with a warning, not throw + Assertions.assertDoesNotThrow(() -> { defaultSerializeClassChecker.loadClass( Thread.currentThread().getContextClassLoader(), Socket.class.getName()); }); @@ -120,6 +121,54 @@ void testAddBlock() { CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST); } + @Test + void testDisallowedClassInWarnModeDoesNotThrow() throws ClassNotFoundException { + SystemPropertyConfigUtils.setSystemProperty( + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, + Runtime.class.getName()); + + SerializeSecurityManager ssm = FrameworkModel.defaultModel() + .getBeanFactory() + .getBean(SerializeSecurityManager.class); + ssm.setCheckStatus(SerializeCheckStatus.WARN); + + DefaultSerializeClassChecker checker = DefaultSerializeClassChecker.getInstance(); + + // WARN mode: disallowed class should be loaded with warning, not throw + Assertions.assertDoesNotThrow(() -> { + checker.loadClass(Thread.currentThread().getContextClassLoader(), Runtime.class.getName()); + }); + Assertions.assertTrue(FrameworkModel.defaultModel() + .getBeanFactory() + .getBean(SerializeSecurityManager.class) + .getWarnedClasses() + .contains(Runtime.class.getName())); + + SystemPropertyConfigUtils.clearSystemProperty( + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST); + } + + @Test + void testDisallowedClassInStrictModeThrows() { + SystemPropertyConfigUtils.setSystemProperty( + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, + Runtime.class.getName()); + + SerializeSecurityManager ssm = FrameworkModel.defaultModel() + .getBeanFactory() + .getBean(SerializeSecurityManager.class); + // Default is STRICT - non-allowed classes should throw + Assertions.assertEquals(SerializeCheckStatus.STRICT, AllowClassNotifyListener.DEFAULT_STATUS); + + DefaultSerializeClassChecker checker = DefaultSerializeClassChecker.getInstance(); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + checker.loadClass(Thread.currentThread().getContextClassLoader(), Runtime.class.getName()); + }); + + SystemPropertyConfigUtils.clearSystemProperty( + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST); + } + @Test void testBlockAll() throws ClassNotFoundException { SystemPropertyConfigUtils.setSystemProperty( From 71d3ae5b9314c10cdacf8d6c5ecb1eda8d437ec1 Mon Sep 17 00:00:00 2001 From: daguimu Date: Fri, 27 Mar 2026 12:09:02 +0800 Subject: [PATCH 2/2] style: apply spotless formatting --- .../utils/DefaultSerializeClassChecker.java | 3 ++- .../utils/DefaultSerializeClassCheckerTest.java | 16 ++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java index 8ac3d6dbdd5..de44530ab60 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/DefaultSerializeClassChecker.java @@ -111,7 +111,8 @@ public Class loadClass(ClassLoader classLoader, String className) throws Clas String msg = "[Serialization Security] Serialized class " + className + " has not implement Serializable interface. " + "Current mode is strict check, will disallow to deserialize it by default. "; - if (checkSerializable && checkStatus != SerializeCheckStatus.WARN + if (checkSerializable + && checkStatus != SerializeCheckStatus.WARN && checkStatus != SerializeCheckStatus.DISABLE) { if (serializeSecurityManager.getWarnedClasses().add(className)) { logger.error(PROTOCOL_UNTRUSTED_SERIALIZE_CLASS, "", "", msg); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java index adffbb50be9..6f318c983ee 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultSerializeClassCheckerTest.java @@ -124,12 +124,10 @@ void testAddBlock() { @Test void testDisallowedClassInWarnModeDoesNotThrow() throws ClassNotFoundException { SystemPropertyConfigUtils.setSystemProperty( - CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, - Runtime.class.getName()); + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, Runtime.class.getName()); - SerializeSecurityManager ssm = FrameworkModel.defaultModel() - .getBeanFactory() - .getBean(SerializeSecurityManager.class); + SerializeSecurityManager ssm = + FrameworkModel.defaultModel().getBeanFactory().getBean(SerializeSecurityManager.class); ssm.setCheckStatus(SerializeCheckStatus.WARN); DefaultSerializeClassChecker checker = DefaultSerializeClassChecker.getInstance(); @@ -151,12 +149,10 @@ void testDisallowedClassInWarnModeDoesNotThrow() throws ClassNotFoundException { @Test void testDisallowedClassInStrictModeThrows() { SystemPropertyConfigUtils.setSystemProperty( - CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, - Runtime.class.getName()); + CommonConstants.DubboProperty.DUBBO_CLASS_DESERIALIZE_BLOCKED_LIST, Runtime.class.getName()); - SerializeSecurityManager ssm = FrameworkModel.defaultModel() - .getBeanFactory() - .getBean(SerializeSecurityManager.class); + SerializeSecurityManager ssm = + FrameworkModel.defaultModel().getBeanFactory().getBean(SerializeSecurityManager.class); // Default is STRICT - non-allowed classes should throw Assertions.assertEquals(SerializeCheckStatus.STRICT, AllowClassNotifyListener.DEFAULT_STATUS);