From 0b49e2f5d860d39def31b70616242da87db74574 Mon Sep 17 00:00:00 2001 From: IceSource Date: Wed, 4 Nov 2026 14:28:30 +0800 Subject: [PATCH] fix: auto-trust array component types during serialization scanning Fixes #16569 --- .../utils/SerializeSecurityConfigurator.java | 7 +- .../src/test/java/com/pojo/ArrayPayload.java | 31 +++++++++ .../SerializeSecurityConfiguratorTest.java | 64 +++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 dubbo-common/src/test/java/com/pojo/ArrayPayload.java diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java index bd11fd8d1218..74ff5355c437 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeSecurityConfigurator.java @@ -302,7 +302,12 @@ private boolean checkClass(Class clazz) { addToAllow(clazz); - if (ClassUtils.isSimpleType(clazz) || clazz.isPrimitive() || clazz.isArray()) { + if (clazz.isArray()) { + checkClass(clazz.getComponentType()); + return true; + } + + if (ClassUtils.isSimpleType(clazz) || clazz.isPrimitive()) { return true; } String className = clazz.getName(); diff --git a/dubbo-common/src/test/java/com/pojo/ArrayPayload.java b/dubbo-common/src/test/java/com/pojo/ArrayPayload.java new file mode 100644 index 000000000000..8d91a449c08e --- /dev/null +++ b/dubbo-common/src/test/java/com/pojo/ArrayPayload.java @@ -0,0 +1,31 @@ +/* + * 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.pojo; + +import java.io.Serializable; + +/** + * Array element with nested and recursive fields for serialization trust tests. + * + * @date 2026-11-04 + */ +public class ArrayPayload implements Serializable { + private Detail detail; + private ArrayPayload[] children; + + public static class Detail implements Serializable {} +} diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityConfiguratorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityConfiguratorTest.java index f4b5be0b9d57..248418c3c07f 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityConfiguratorTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeSecurityConfiguratorTest.java @@ -28,7 +28,10 @@ import java.util.Map; import java.util.Set; import java.util.Vector; +import java.util.stream.Stream; +import com.pojo.ArrayPayload; +import com.pojo.Demo1; import com.service.DemoService1; import com.service.DemoService2; import com.service.DemoService4; @@ -39,9 +42,70 @@ import com.service.deep1.deep2.deep3.DemoService3; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; class SerializeSecurityConfiguratorTest { + @ParameterizedTest + @MethodSource("arrayServices") + void testArrayComponentTypes(Class serviceInterface) throws ClassNotFoundException { + FrameworkModel frameworkModel = new FrameworkModel(); + try { + ApplicationModel applicationModel = frameworkModel.newApplication(); + ApplicationConfig applicationConfig = new ApplicationConfig("array-test"); + applicationConfig.setSerializeCheckStatus(SerializeCheckStatus.STRICT.name()); + applicationModel.getApplicationConfigManager().setApplication(applicationConfig); + ModuleModel moduleModel = applicationModel.newModule(); + SerializeSecurityConfigurator configurator = new SerializeSecurityConfigurator(moduleModel); + DefaultSerializeClassChecker checker = + frameworkModel.getBeanFactory().getBean(DefaultSerializeClassChecker.class); + SerializeSecurityManager manager = frameworkModel.getBeanFactory().getBean(SerializeSecurityManager.class); + ClassLoader classLoader = ArrayPayload.class.getClassLoader(); + + Assertions.assertThrows( + IllegalArgumentException.class, () -> checker.loadClass(classLoader, ArrayPayload.class.getName())); + + configurator.registerInterface(serviceInterface); + + Assertions.assertSame(ArrayPayload.class, checker.loadClass(classLoader, ArrayPayload.class.getName())); + Assertions.assertTrue(manager.getAllowedPrefix().contains(ArrayPayload.Detail.class.getName())); + IllegalArgumentException failure = Assertions.assertThrows( + IllegalArgumentException.class, () -> checker.loadClass(classLoader, Demo1.class.getName())); + Assertions.assertTrue(failure.getMessage().contains("is not in allow list")); + } finally { + frameworkModel.destroy(); + } + } + + static Stream> arrayServices() { + return Stream.of( + ArrayParameterService.class, + ArrayReturnService.class, + MultidimensionalArrayService.class, + ArrayFieldService.class); + } + + interface ArrayParameterService { + void accept(ArrayPayload[] values); + } + + interface ArrayReturnService { + ArrayPayload[] read(); + } + + interface MultidimensionalArrayService { + ArrayPayload[][] read(); + } + + interface ArrayFieldService { + ArrayEnvelope read(); + } + + static class ArrayEnvelope { + private ArrayPayload[] values; + } + @Test void test() { FrameworkModel frameworkModel = new FrameworkModel();