diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java index 0fc5fa90d3..26b907dcd2 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParser.java @@ -16,26 +16,25 @@ */ package org.apache.dubbo.rpc.cluster.configurator.parser; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONValidator; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; -import org.yaml.snakeyaml.TypeDescription; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONValidator; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE; import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY; +import static org.apache.dubbo.rpc.cluster.Constants.OVERRIDE_PROVIDERS_KEY; /** * Config parser @@ -72,14 +71,10 @@ public class ConfigParser { return urls; } - private static T parseObject(String rawConfig) { - Constructor constructor = new Constructor(ConfiguratorConfig.class); - TypeDescription itemDescription = new TypeDescription(ConfiguratorConfig.class); - itemDescription.addPropertyParameters("items", ConfigItem.class); - constructor.addTypeDescription(itemDescription); - - Yaml yaml = new Yaml(constructor); - return yaml.load(rawConfig); + private static ConfiguratorConfig parseObject(String rawConfig) { + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawConfig); + return ConfiguratorConfig.parseFromMap(map); } private static List serviceItemToUrls(ConfigItem item, ConfiguratorConfig config) { diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java index 2a6a56a3c8..c5b1263de6 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfigItem.java @@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.configurator.parser.model; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * @@ -37,6 +38,50 @@ public class ConfigItem { private Map parameters; private String side; + @SuppressWarnings("unchecked") + public static ConfigItem parseFromMap(Map map) { + ConfigItem configItem = new ConfigItem(); + configItem.setType((String) map.get("type")); + + Object enabled = map.get("enabled"); + if (enabled != null) { + configItem.setEnabled(Boolean.parseBoolean(enabled.toString())); + } + + Object addresses = map.get("addresses"); + if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) { + configItem.setAddresses(((List) addresses).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + Object providerAddresses = map.get("providerAddresses"); + if (providerAddresses != null && List.class.isAssignableFrom(providerAddresses.getClass())) { + configItem.setProviderAddresses(((List) providerAddresses).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + Object services = map.get("services"); + if (services != null && List.class.isAssignableFrom(services.getClass())) { + configItem.setServices(((List) services).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + Object applications = map.get("applications"); + if (applications != null && List.class.isAssignableFrom(applications.getClass())) { + configItem.setApplications(((List) applications).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + Object parameters = map.get("parameters"); + if (parameters != null && Map.class.isAssignableFrom(parameters.getClass())) { + configItem.setParameters(((Map) parameters).entrySet() + .stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString()))); + } + + configItem.setSide((String) map.get("side")); + return configItem; + } + public String getType() { return type; } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfiguratorConfig.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfiguratorConfig.java index aadc999776..91a48636ba 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfiguratorConfig.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/configurator/parser/model/ConfiguratorConfig.java @@ -17,6 +17,8 @@ package org.apache.dubbo.rpc.cluster.configurator.parser.model; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @@ -31,6 +33,26 @@ public class ConfiguratorConfig { private Boolean enabled = true; private List configs; + @SuppressWarnings("unchecked") + public static ConfiguratorConfig parseFromMap(Map map) { + ConfiguratorConfig configuratorConfig = new ConfiguratorConfig(); + configuratorConfig.setConfigVersion((String) map.get("configVersion")); + configuratorConfig.setScope((String) map.get("scope")); + configuratorConfig.setKey((String) map.get("key")); + + Object enabled = map.get("enabled"); + if (enabled != null) { + configuratorConfig.setEnabled(Boolean.parseBoolean(enabled.toString())); + } + + Object configs = map.get("configs"); + if (configs != null && List.class.isAssignableFrom(configs.getClass())) { + configuratorConfig.setConfigs(((List>) configs).stream() + .map(ConfigItem::parseFromMap).collect(Collectors.toList())); + } + + return configuratorConfig; + } public String getConfigVersion() { return configVersion; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java index b5755e9f15..e12b8d8685 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/AbstractRouterRule.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.rpc.cluster.router; +import java.util.Map; + /** * TODO Extract more code here if necessary */ @@ -31,6 +33,43 @@ public abstract class AbstractRouterRule { private String scope; private String key; + protected void parseFromMap0(Map map) { + setRawRule((String) map.get("rawRule")); + + Object runtime = map.get("runtime"); + if (runtime != null) { + setRuntime(Boolean.parseBoolean(runtime.toString())); + } + + Object force = map.get("force"); + if (force != null) { + setForce(Boolean.parseBoolean(force.toString())); + } + + Object valid = map.get("valid"); + if (valid != null) { + setValid(Boolean.parseBoolean(valid.toString())); + } + + Object enabled = map.get("enabled"); + if (enabled != null) { + setEnabled(Boolean.parseBoolean(enabled.toString())); + } + + Object priority = map.get("priority"); + if (priority != null) { + setPriority(Integer.parseInt(priority.toString())); + } + + Object dynamic = map.get("dynamic"); + if (dynamic != null) { + setDynamic(Boolean.parseBoolean(dynamic.toString())); + } + + setScope((String) map.get("scope")); + setKey((String) map.get("key")); + } + public String getRawRule() { return rawRule; } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java index 7455585fe5..b94a5ce486 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRouterRule.java @@ -19,15 +19,31 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model; import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * */ public class ConditionRouterRule extends AbstractRouterRule { - public ConditionRouterRule() { + private List conditions; + + @SuppressWarnings("unchecked") + public static ConditionRouterRule parseFromMap(Map map) { + ConditionRouterRule conditionRouterRule = new ConditionRouterRule(); + conditionRouterRule.parseFromMap0(map); + + Object conditions = map.get("conditions"); + if (conditions != null && List.class.isAssignableFrom(conditions.getClass())) { + conditionRouterRule.setConditions(((List) conditions).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + return conditionRouterRule; } - private List conditions; + public ConditionRouterRule() { + } public List getConditions() { return conditions; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java index 07cc7ede2d..3d6c53ac57 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/model/ConditionRuleParser.java @@ -19,7 +19,9 @@ package org.apache.dubbo.rpc.cluster.router.condition.config.model; import org.apache.dubbo.common.utils.CollectionUtils; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +import java.util.Map; /** * %YAML1.2 @@ -38,10 +40,9 @@ import org.yaml.snakeyaml.constructor.Constructor; public class ConditionRuleParser { public static ConditionRouterRule parse(String rawRule) { - Constructor constructor = new Constructor(ConditionRouterRule.class); - - Yaml yaml = new Yaml(constructor); - ConditionRouterRule rule = yaml.load(rawRule); + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawRule); + ConditionRouterRule rule = ConditionRouterRule.parseFromMap(map); rule.setRawRule(rawRule); if (CollectionUtils.isEmpty(rule.getConditions())) { rule.setValid(false); diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java index df94f6bc99..4c2759689d 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/script/ScriptRouter.java @@ -32,6 +32,13 @@ import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -62,6 +69,17 @@ public class ScriptRouter extends AbstractRouter { private CompiledScript function; + private AccessControlContext accessControlContext; + + { + //Just give permission of reflect to access member. + Permissions perms = new Permissions(); + perms.add(new RuntimePermission("accessDeclaredMembers")); + // Cast to Certificate[] required because of ambiguity: + ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms); + accessControlContext = new AccessControlContext(new ProtectionDomain[]{domain}); + } + public ScriptRouter(URL url) { this.url = url; this.priority = url.getParameter(PRIORITY_KEY, SCRIPT_ROUTER_DEFAULT_PRIORITY); @@ -75,8 +93,6 @@ public class ScriptRouter extends AbstractRouter { logger.error("route error, rule has been ignored. rule: " + rule + ", url: " + RpcContext.getServiceContext().getUrl(), e); } - - } /** @@ -107,17 +123,19 @@ public class ScriptRouter extends AbstractRouter { @Override public List> route(List> invokers, URL url, Invocation invocation) throws RpcException { - try { - Bindings bindings = createBindings(invokers, invocation); - if (function == null) { - return invokers; - } - return getRoutedInvokers(function.eval(bindings)); - } catch (ScriptException e) { - logger.error("route error, rule has been ignored. rule: " + rule + ", method:" + - invocation.getMethodName() + ", url: " + RpcContext.getServiceContext().getUrl(), e); + if (engine == null || function == null) { return invokers; } + Bindings bindings = createBindings(invokers, invocation); + return getRoutedInvokers(AccessController.doPrivileged((PrivilegedAction) () -> { + try { + return function.eval(bindings); + } catch (ScriptException e) { + logger.error("route error, rule has been ignored. rule: " + rule + ", method:" + + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e); + return invokers; + } + }, accessControlContext)); } /** diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java index 28628a5d63..d51482ef4f 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/Tag.java @@ -17,6 +17,8 @@ package org.apache.dubbo.rpc.cluster.router.tag.model; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @@ -25,6 +27,19 @@ public class Tag { private String name; private List addresses; + @SuppressWarnings("unchecked") + public static Tag parseFromMap(Map map) { + Tag tag = new Tag(); + tag.setName((String) map.get("name")); + + Object addresses = map.get("addresses"); + if (addresses != null && List.class.isAssignableFrom(addresses.getClass())) { + tag.setAddresses(((List) addresses).stream().map(String::valueOf).collect(Collectors.toList())); + } + + return tag; + } + public String getName() { return name; } diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java index 154a161a21..259456ee93 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRouterRule.java @@ -46,6 +46,20 @@ public class TagRouterRule extends AbstractRouterRule { private Map> addressToTagnames = new HashMap<>(); private Map> tagnameToAddresses = new HashMap<>(); + @SuppressWarnings("unchecked") + public static TagRouterRule parseFromMap(Map map) { + TagRouterRule tagRouterRule = new TagRouterRule(); + tagRouterRule.parseFromMap0(map); + + Object tags = map.get("tags"); + if (tags != null && List.class.isAssignableFrom(tags.getClass())) { + tagRouterRule.setTags(((List>) tags).stream() + .map(Tag::parseFromMap).collect(Collectors.toList())); + } + + return tagRouterRule; + } + public void init() { if (!isValid()) { return; diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java index 4f6669f1b2..b4cda311c1 100644 --- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java +++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/model/TagRuleParser.java @@ -17,9 +17,11 @@ package org.apache.dubbo.rpc.cluster.router.tag.model; import org.apache.dubbo.common.utils.CollectionUtils; -import org.yaml.snakeyaml.TypeDescription; + import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +import java.util.Map; /** * @@ -27,13 +29,9 @@ import org.yaml.snakeyaml.constructor.Constructor; public class TagRuleParser { public static TagRouterRule parse(String rawRule) { - Constructor constructor = new Constructor(TagRouterRule.class); - TypeDescription tagDescription = new TypeDescription(TagRouterRule.class); - tagDescription.addPropertyParameters("tags", Tag.class); - constructor.addTypeDescription(tagDescription); - - Yaml yaml = new Yaml(constructor); - TagRouterRule rule = yaml.load(rawRule); + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawRule); + TagRouterRule rule = TagRouterRule.parseFromMap(map); rule.setRawRule(rawRule); if (CollectionUtils.isEmpty(rule.getTags())) { rule.setValid(false); diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory index 1890efca1a..978415861b 100644 --- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory +++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory @@ -1,5 +1,4 @@ file=org.apache.dubbo.rpc.cluster.router.file.FileRouterFactory -script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory condition=org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java index 7825b37bc3..3b10d5b9f8 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/configurator/parser/ConfigParserTest.java @@ -17,18 +17,17 @@ package org.apache.dubbo.rpc.cluster.configurator.parser; import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfigItem; import org.apache.dubbo.rpc.cluster.configurator.parser.model.ConfiguratorConfig; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY; import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY; @@ -49,14 +48,9 @@ public class ConfigParserTest { @Test public void snakeYamlBasicTest() throws IOException { try (InputStream yamlStream = this.getClass().getResourceAsStream("/ServiceNoApp.yml")) { - - Constructor constructor = new Constructor(ConfiguratorConfig.class); - TypeDescription carDescription = new TypeDescription(ConfiguratorConfig.class); - carDescription.addPropertyParameters("items", ConfigItem.class); - constructor.addTypeDescription(carDescription); - - Yaml yaml = new Yaml(constructor); - ConfiguratorConfig config = yaml.load(yamlStream); + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(yamlStream); + ConfiguratorConfig config = ConfiguratorConfig.parseFromMap(map); System.out.println(config); } } diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java index 0981719d47..fda8dae929 100644 --- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java +++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/TagRouterTest.java @@ -97,6 +97,8 @@ public class TagRouterTest { TagRouterRule tagRouterRule = TagRuleParser.parse(tagRouterRuleConfig); // assert tags + assert tagRouterRule.getKey().equals("demo-provider"); + assert tagRouterRule.getPriority() == 1; assert tagRouterRule.getTagNames().contains("tag1"); assert tagRouterRule.getTagNames().contains("tag2"); assert tagRouterRule.getTagNames().contains("tag3"); diff --git a/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory new file mode 100644 index 0000000000..dc43350460 --- /dev/null +++ b/dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory @@ -0,0 +1 @@ +script=org.apache.dubbo.rpc.cluster.router.script.ScriptRouterFactory diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java index 5bd2970847..3ca12467ff 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URLStrParser.java @@ -109,6 +109,12 @@ public final class URLStrParser { */ private static URL parseURLBody(String fullURLStr, String decodedBody, Map parameters, boolean modifiable) { int starIdx = 0, endIdx = decodedBody.length(); + // ignore the url content following '#' + int poundIndex = decodedBody.indexOf('#'); + if (poundIndex != -1) { + endIdx = poundIndex; + } + String protocol = null; int protoEndIdx = decodedBody.indexOf("://"); if (protoEndIdx >= 0) { @@ -132,7 +138,7 @@ public final class URLStrParser { String path = null; int pathStartIdx = indexOf(decodedBody, '/', starIdx, endIdx); if (pathStartIdx >= 0) { - path = decodedBody.substring(pathStartIdx + 1); + path = decodedBody.substring(pathStartIdx + 1, endIdx); endIdx = pathStartIdx; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java index d37335eb64..c6610cb23a 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java @@ -20,6 +20,7 @@ import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.LogHelper; import org.apache.dubbo.common.utils.ReflectUtils; +import org.apache.dubbo.common.utils.SerializeClassChecker; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -464,6 +465,7 @@ public final class JavaBeanSerializeUtil { if (isReferenceType(name)) { name = name.substring(1, name.length() - 1); } + SerializeClassChecker.getInstance().validateClass(name); return Class.forName(name, false, loader); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java index e510847990..9119f323e5 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java @@ -377,6 +377,16 @@ public interface CommonConstants { */ String DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH = "META-INF/dubbo/service-name-mapping.properties"; + String CLASS_DESERIALIZE_BLOCK_ALL = "dubbo.security.serialize.blockAllClassExceptAllow"; + + String CLASS_DESERIALIZE_ALLOWED_LIST = "dubbo.security.serialize.allowedClassList"; + + String CLASS_DESERIALIZE_BLOCKED_LIST = "dubbo.security.serialize.blockedClassList"; + + String ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE = "dubbo.security.serialize.generic.native-java-enable"; + + String SERIALIZE_BLOCKED_LIST_FILE_PATH = "security/serialize.blockedlist"; + String QOS_LIVE_PROBE_EXTENSION = "dubbo.application.liveness-probe"; String QOS_READY_PROBE_EXTENSION = "dubbo.application.readiness-probe"; 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 2a6b73ed0b..145550c4ef 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 @@ -386,6 +386,7 @@ public class PojoUtils { if (pojo instanceof Map && type != null) { Object className = ((Map) pojo).get("class"); if (className instanceof String) { + SerializeClassChecker.getInstance().validateClass((String) className); try { type = ClassUtils.forName((String) className); } catch (ClassNotFoundException e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java new file mode 100644 index 0000000000..cb5c2f04cc --- /dev/null +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/SerializeClassChecker.java @@ -0,0 +1,154 @@ +/* + * 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.dubbo.common.utils; + +import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST; +import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST; +import static org.apache.dubbo.common.constants.CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL; +import static org.apache.dubbo.common.constants.CommonConstants.SERIALIZE_BLOCKED_LIST_FILE_PATH; + +public class SerializeClassChecker { + private static final Logger logger = LoggerFactory.getLogger(SerializeClassChecker.class); + + private static volatile SerializeClassChecker INSTANCE = null; + + private final boolean BLOCK_ALL_CLASS_EXCEPT_ALLOW; + private final Set CLASS_DESERIALIZE_ALLOWED_SET = new ConcurrentHashSet<>(); + private final Set CLASS_DESERIALIZE_BLOCKED_SET = new ConcurrentHashSet<>(); + + private final Object CACHE = new Object(); + private final LFUCache CLASS_ALLOW_LFU_CACHE = new LFUCache<>(); + private final LFUCache CLASS_BLOCK_LFU_CACHE = new LFUCache<>(); + + private final AtomicLong counter = new AtomicLong(0); + + private SerializeClassChecker() { + String blockAllClassExceptAllow = System.getProperty(CLASS_DESERIALIZE_BLOCK_ALL, "false"); + BLOCK_ALL_CLASS_EXCEPT_ALLOW = Boolean.parseBoolean(blockAllClassExceptAllow); + + String[] lines; + try { + ClassLoader classLoader = ClassUtils.getClassLoader(JavaBeanSerializeUtil.class); + if (classLoader != null) { + lines = IOUtils.readLines(classLoader.getResourceAsStream(SERIALIZE_BLOCKED_LIST_FILE_PATH)); + } else { + lines = IOUtils.readLines(ClassLoader.getSystemResourceAsStream(SERIALIZE_BLOCKED_LIST_FILE_PATH)); + } + for (String line : lines) { + line = line.trim(); + if (StringUtils.isEmpty(line) || line.startsWith("#")) { + continue; + } + CLASS_DESERIALIZE_BLOCKED_SET.add(line); + } + + } catch (IOException e) { + logger.error("Failed to load blocked class list! Will ignore default blocked list.", e); + } + + String allowedClassList = System.getProperty(CLASS_DESERIALIZE_ALLOWED_LIST, "").trim().toLowerCase(Locale.ROOT); + String blockedClassList = System.getProperty(CLASS_DESERIALIZE_BLOCKED_LIST, "").trim().toLowerCase(Locale.ROOT); + + if (StringUtils.isNotEmpty(allowedClassList)) { + String[] classStrings = allowedClassList.trim().split(","); + CLASS_DESERIALIZE_ALLOWED_SET.addAll(Arrays.asList(classStrings)); + } + + if (StringUtils.isNotEmpty(blockedClassList)) { + String[] classStrings = blockedClassList.trim().split(","); + CLASS_DESERIALIZE_BLOCKED_SET.addAll(Arrays.asList(classStrings)); + } + + } + + public static SerializeClassChecker getInstance() { + if (INSTANCE == null) { + synchronized (SerializeClassChecker.class) { + if (INSTANCE == null) { + INSTANCE = new SerializeClassChecker(); + } + } + } + return INSTANCE; + } + + /** + * For ut only + */ + @Deprecated + protected static void clearInstance() { + INSTANCE = null; + } + + /** + * Check if a class is in block list, using prefix match + * + * @throws IllegalArgumentException if class is blocked + * @param name class name ( all are convert to lower case ) + */ + public void validateClass(String name) { + name = name.toLowerCase(Locale.ROOT); + if (CACHE == CLASS_ALLOW_LFU_CACHE.get(name)) { + return; + } + + if (CACHE == CLASS_BLOCK_LFU_CACHE.get(name)) { + error(name); + } + + for (String allowedPrefix : CLASS_DESERIALIZE_ALLOWED_SET) { + if (name.startsWith(allowedPrefix)) { + CLASS_ALLOW_LFU_CACHE.put(name, CACHE); + return; + } + } + + for (String blockedPrefix : CLASS_DESERIALIZE_BLOCKED_SET) { + if (BLOCK_ALL_CLASS_EXCEPT_ALLOW || name.startsWith(blockedPrefix)) { + CLASS_BLOCK_LFU_CACHE.put(name, CACHE); + error(name); + } + } + + CLASS_ALLOW_LFU_CACHE.put(name, CACHE); + } + + private void error(String name) { + String notice = "Trigger the safety barrier! " + + "Catch not allowed serialize class. " + + "Class name: " + name + " . " + + "This means currently maybe being attacking by others." + + "If you are sure this is a mistake, " + + "please add this class name to `" + CLASS_DESERIALIZE_ALLOWED_LIST + + "` as a system environment property."; + if (counter.incrementAndGet() % 1000 == 0 || counter.get() < 100) { + logger.error(notice); + } + throw new IllegalArgumentException(notice); + } + +} diff --git a/dubbo-common/src/main/resources/security/serialize.blockedlist b/dubbo-common/src/main/resources/security/serialize.blockedlist new file mode 100644 index 0000000000..de0b68de63 --- /dev/null +++ b/dubbo-common/src/main/resources/security/serialize.blockedlist @@ -0,0 +1,167 @@ +# +# +# 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. +# +# +aj.org.objectweb.asm. +br.com.anteros. +ch.qos.logback. +clojure.core$constantly +clojure.main$eval_opt +com.alibaba.citrus.springext.support.parser.abstractnamedproxybeandefinitionparser$proxytargetfactory +com.alibaba.citrus.springext.util.springextutil.abstractproxy +com.alibaba.druid.pool.druiddatasource +com.alibaba.druid.stat.jdbcdatasourcestat +com.alibaba.fastjson.annotation +com.alipay.custrelation.service.model.redress.pair +com.caucho. +com.ibatis. +com.mchange +com.mysql.cj.jdbc.admin. +com.mysql.cj.jdbc.mysqlconnectionpooldatasource +com.mysql.cj.jdbc.mysqldatasource +com.mysql.cj.jdbc.mysqlxadatasource +com.mysql.cj.log. +com.p6spy.engine. +com.rometools.rome.feed.impl.equalsbean +com.rometools.rome.feed.impl.tostringbean +com.sun. +com.taobao.eagleeye.wrapper +com.zaxxer.hikari. +flex.messaging.util.concurrent. +java.awt.i +java.awt.p +java.beans.expression +java.io.closeable +java.io.serializable +java.lang.autocloseable +java.lang.class +java.lang.cloneable +java.lang.iterable +java.lang.object +java.lang.readable +java.lang.runnable +java.lang.thread +java.lang.unixprocess +java.net.inetaddress +java.net.socket +java.net.url +java.rmi +java.security.signedobject +java.util.collection +java.util.eventlistener +java.util.jar. +java.util.logging. +java.util.prefs. +java.util.serviceloader$lazyiterator +javassist. +javax.activation. +javax.imageio.imageio$containsfilter +javax.imageio.spi.serviceregistry +javax.management. +javax.naming. +javax.net. +javax.print. +javax.script. +javax.sound. +javax.swing.j +javax.tools. +javax.xml +jdk.internal. +jodd.db.connection. +junit. +net.bytebuddy.dynamic.loading.bytearrayclassloader +net.sf.cglib. +net.sf.ehcache.hibernate. +net.sf.ehcache.transaction.manager. +oracle.jdbc. +oracle.jms.aq +oracle.net +org.aoju.bus.proxy.provider. +org.apache.activemq.activemqconnectionfactory +org.apache.activemq.activemqxaconnectionfactory +org.apache.activemq.jms.pool. +org.apache.activemq.pool. +org.apache.activemq.spring. +org.apache.aries.transaction. +org.apache.axis2.jaxws.spi.handler. +org.apache.axis2.transport.jms. +org.apache.bcel +org.apache.carbondata.core.scan.expression.expressionresult +org.apache.catalina. +org.apache.cocoon. +org.apache.commons.beanutils +org.apache.commons.collections.comparators. +org.apache.commons.collections.functors +org.apache.commons.collections.functors. +org.apache.commons.collections.transformer +org.apache.commons.collections4.comparators +org.apache.commons.collections4.functors +org.apache.commons.collections4.transformer +org.apache.commons.configuration +org.apache.commons.dbcp +org.apache.commons.fileupload +org.apache.commons.jelly. +org.apache.commons.logging. +org.apache.commons.proxy. +org.apache.cxf.jaxrs.provider. +org.apache.hadoop.shaded.com.zaxxer.hikari. +org.apache.http.auth. +org.apache.http.conn. +org.apache.http.cookie. +org.apache.http.impl. +org.apache.ibatis.datasource +org.apache.ibatis.executor. +org.apache.ibatis.javassist. +org.apache.ibatis.ognl. +org.apache.ibatis.parsing. +org.apache.ibatis.reflection. +org.apache.ibatis.scripting. +org.apache.ignite.cache.jta. +org.apache.log4j. +org.apache.logging. +org.apache.myfaces.context.servlet +org.apache.openjpa.ee. +org.apache.shiro.jndi. +org.apache.shiro.realm. +org.apache.tomcat +org.apache.wicket.util +org.apache.xalan +org.apache.xbean. +org.apache.xpath.xpathcontext +org.codehaus.groovy.runtime +org.codehaus.jackson. +org.eclipse.jetty. +org.geotools.filter.constantexpression +org.h2.jdbcx. +org.h2.server. +org.hibernate +org.javasimon. +org.jaxen. +org.jboss +org.jdom. +org.jdom2.transform. +org.logicalcobwebs. +org.mortbay.jetty. +org.mozilla.javascript +org.objectweb.asm. +org.osjava.sj. +org.python.core +org.quartz. +org.slf4j. +org.springframework. +org.yaml.snakeyaml.tokens.directivetoken +sun.rmi.server.unicastref \ No newline at end of file diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java index b48dbdb8f3..916da580ec 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/URLTest.java @@ -38,6 +38,22 @@ import static org.junit.jupiter.api.Assertions.fail; public class URLTest { + @Test + public void test_ignore_pond() { + URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path#index?version=1.0.0&id=org.apache.dubbo.config.RegistryConfig#0"); + assertURLStrDecoder(url); + assertEquals("dubbo", url.getProtocol()); + assertEquals("admin", url.getUsername()); + assertEquals("hello1234", url.getPassword()); + assertEquals("10.20.130.230", url.getHost()); + assertEquals("10.20.130.230:20880", url.getAddress()); + assertEquals(20880, url.getPort()); + assertEquals("context/path", url.getPath()); + assertEquals(2, url.getParameters().size()); + assertEquals("1.0.0", url.getVersion()); + assertEquals("org.apache.dubbo.config.RegistryConfig#0", url.getParameter("id")); + } + @Test public void test_valueOf_noProtocolAndHost() throws Exception { URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.java new file mode 100644 index 0000000000..b58f399808 --- /dev/null +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/SerializeClassCheckerTest.java @@ -0,0 +1,105 @@ +/* + * 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.dubbo.common.utils; + +import org.apache.dubbo.common.constants.CommonConstants; + +import javassist.compiler.Javac; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.Socket; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +public class SerializeClassCheckerTest { + + @BeforeEach + public void setUp() { + SerializeClassChecker.clearInstance(); + } + + @Test + public void testCommon() { + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(List.class.getName()); + serializeClassChecker.validateClass(LinkedList.class.getName()); + serializeClassChecker.validateClass(Integer.class.getName()); + serializeClassChecker.validateClass(int.class.getName()); + + serializeClassChecker.validateClass(List.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(LinkedList.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(Integer.class.getName().toUpperCase(Locale.ROOT)); + serializeClassChecker.validateClass(int.class.getName().toUpperCase(Locale.ROOT)); + } + + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Socket.class.getName()); + }); + } + + @Test + public void testAddAllow() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, Socket.class.getName() + "," + Javac.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(Socket.class.getName()); + serializeClassChecker.validateClass(Javac.class.getName()); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST); + } + + @Test + public void testAddBlock() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST, LinkedList.class.getName() + "," + Integer.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(LinkedList.class.getName()); + }); + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Integer.class.getName()); + }); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCKED_LIST); + } + + @Test + public void testBlockAll() { + System.setProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL, "true"); + System.setProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST, LinkedList.class.getName()); + + SerializeClassChecker serializeClassChecker = SerializeClassChecker.getInstance(); + for (int i = 0; i < 10; i++) { + serializeClassChecker.validateClass(LinkedList.class.getName()); + Assertions.assertThrows(IllegalArgumentException.class, ()-> { + serializeClassChecker.validateClass(Integer.class.getName()); + }); + } + + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_BLOCK_ALL); + System.clearProperty(CommonConstants.CLASS_DESERIALIZE_ALLOWED_LIST); + } +} diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/ApplicationMigrationRule.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/ApplicationMigrationRule.java index 00e2a9fa13..05b148364d 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/ApplicationMigrationRule.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/ApplicationMigrationRule.java @@ -16,11 +16,30 @@ */ package org.apache.dubbo.registry.client.migration.model; +import java.util.Map; + public class ApplicationMigrationRule { private String name; private MigrationStep step; private Float threshold; + public static ApplicationMigrationRule parseFromMap(Map map) { + ApplicationMigrationRule applicationMigrationRule = new ApplicationMigrationRule(); + applicationMigrationRule.setName((String) map.get("name")); + + Object step = map.get("step"); + if (step != null) { + applicationMigrationRule.setStep(MigrationStep.valueOf(step.toString())); + } + + Object threshold = map.get("threshold"); + if (threshold != null) { + applicationMigrationRule.setThreshold(Float.valueOf(threshold.toString())); + } + + return applicationMigrationRule; + } + public ApplicationMigrationRule() { } diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/InterfaceMigrationRule.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/InterfaceMigrationRule.java index a59b9d9133..614cbbd115 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/InterfaceMigrationRule.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/InterfaceMigrationRule.java @@ -16,12 +16,32 @@ */ package org.apache.dubbo.registry.client.migration.model; +import java.util.Map; + public class InterfaceMigrationRule { private String appName; private String serviceKey; private MigrationStep step; private Float threshold; + public static InterfaceMigrationRule parseFromMap(Map map) { + InterfaceMigrationRule interfaceMigrationRule = new InterfaceMigrationRule(); + interfaceMigrationRule.setAppName((String) map.get("appName")); + interfaceMigrationRule.setServiceKey((String) map.get("serviceKey")); + + Object step = map.get("step"); + if (step != null) { + interfaceMigrationRule.setStep(MigrationStep.valueOf(step.toString())); + } + + Object threshold = map.get("threshold"); + if (threshold != null) { + interfaceMigrationRule.setThreshold(Float.valueOf(threshold.toString())); + } + + return interfaceMigrationRule; + } + public InterfaceMigrationRule(){} public InterfaceMigrationRule(String appName, String serviceKey, MigrationStep step, Float threshold) { diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java index 69753ffb8d..755273f600 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/model/MigrationRule.java @@ -20,12 +20,14 @@ import org.apache.dubbo.common.utils.CollectionUtils; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * # key = demo-consumer.migration @@ -55,6 +57,42 @@ public class MigrationRule { private transient Map interfaceRules; private transient Map applicationRules; + @SuppressWarnings("unchecked") + private static MigrationRule parseFromMap(Map map) { + MigrationRule migrationRule = new MigrationRule(); + migrationRule.setKey((String) map.get("key")); + + Object step = map.get("step"); + if (step != null) { + migrationRule.setStep(MigrationStep.valueOf(step.toString())); + } + + Object threshold = map.get("threshold"); + if (threshold != null) { + migrationRule.setThreshold(Float.valueOf(threshold.toString())); + } + + Object targetIps = map.get("targetIps"); + if (targetIps != null && List.class.isAssignableFrom(targetIps.getClass())) { + migrationRule.setTargetIps(((List) targetIps).stream() + .map(String::valueOf).collect(Collectors.toList())); + } + + Object interfaces = map.get("interfaces"); + if (interfaces != null && List.class.isAssignableFrom(interfaces.getClass())) { + migrationRule.setInterfaces(((List>) interfaces).stream() + .map(InterfaceMigrationRule::parseFromMap).collect(Collectors.toList())); + } + + Object applications = map.get("applications"); + if (applications != null && List.class.isAssignableFrom(applications.getClass())) { + migrationRule.setApplications(((List>) applications).stream() + .map(ApplicationMigrationRule::parseFromMap).collect(Collectors.toList())); + } + + return migrationRule; + } + public MigrationRule() { } @@ -233,9 +271,9 @@ public class MigrationRule { } public static MigrationRule parse(String rawRule) { - Constructor constructor = new Constructor(MigrationRule.class); - Yaml yaml = new Yaml(constructor); - return yaml.load(rawRule); + Yaml yaml = new Yaml(new SafeConstructor()); + Map map = yaml.load(rawRule); + return parseFromMap(map); } public static String toYaml(MigrationRule rule) { diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/model/MigrationRuleTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/model/MigrationRuleTest.java new file mode 100644 index 0000000000..1e2afe26f4 --- /dev/null +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/migration/model/MigrationRuleTest.java @@ -0,0 +1,48 @@ +/* + * 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.dubbo.registry.client.migration.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MigrationRuleTest { + + @Test + public void test_parse() { + String rule = "key: demo-consumer\n" + + "step: APPLICATION_FIRST\n" + + "threshold: 1.0\n" + + "interfaces:\n" + + " - serviceKey: DemoService:1.0.0\n" + + " threshold: 1.0\n" + + " step: APPLICATION_FIRST\n" + + " - serviceKey: GreetingService:1.0.0\n" + + " step: FORCE_APPLICATION"; + + MigrationRule migrationRule = MigrationRule.parse(rule); + assertEquals(migrationRule.getKey(), "demo-consumer"); + assertEquals(migrationRule.getStep(), MigrationStep.APPLICATION_FIRST); + assertEquals(migrationRule.getThreshold(), 1.0f); + assertEquals(migrationRule.getInterfaces().size(), 2); + assertNotNull(migrationRule.getInterfaceRule("DemoService:1.0.0")); + assertNotNull(migrationRule.getInterfaceRule("GreetingService:1.0.0")); + assertNull(migrationRule.getApplications()); + } +} diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java index ed5a01f883..cb37de4f49 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java @@ -19,11 +19,14 @@ package org.apache.dubbo.rpc.filter; import org.apache.dubbo.common.beanutil.JavaBeanAccessor; import org.apache.dubbo.common.beanutil.JavaBeanDescriptor; import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil; +import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; import org.apache.dubbo.common.io.UnsafeByteArrayOutputStream; +import org.apache.dubbo.common.logger.Logger; +import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.serialize.Serialization; import org.apache.dubbo.common.utils.PojoUtils; import org.apache.dubbo.common.utils.ReflectUtils; @@ -35,6 +38,7 @@ import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcException; import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; import org.apache.dubbo.rpc.support.ProtocolUtils; @@ -54,6 +58,7 @@ import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; */ @Activate(group = CommonConstants.PROVIDER, order = -20000) public class GenericFilter implements Filter, Filter.Listener { + private final Logger logger = LoggerFactory.getLogger(GenericFilter.class); @Override public Result invoke(Invoker invoker, Invocation inv) throws RpcException { @@ -85,6 +90,18 @@ public class GenericFilter implements Filter, Filter.Listener { || ProtocolUtils.isGenericReturnRawResult(generic)) { args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { + Configuration configuration = ApplicationModel.getEnvironment().getConfiguration(); + if (!configuration.getBoolean(CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, false)) { + String notice = "Trigger the safety barrier! " + + "Native Java Serializer is not allowed by default." + + "This means currently maybe being attacking by others. " + + "If you are sure this is a mistake, " + + "please set `" + CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE + "` enable in configuration! " + + "Before doing so, please make sure you have configure JEP290 to prevent serialization attack."; + logger.error(notice); + throw new RpcException(new IllegalStateException(notice)); + } + for (int i = 0; i < args.length; i++) { if (byte[].class == args[i].getClass()) { try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) { diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java index c49aa9278f..e2d2b3dea2 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; +import static org.apache.dubbo.common.constants.CommonConstants.ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE; import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_SERIALIZATION_NATIVE_JAVA; import static org.apache.dubbo.rpc.Constants.GENERIC_KEY; import static org.mockito.ArgumentMatchers.any; @@ -75,6 +76,8 @@ public class GenericFilterTest { @Test public void testInvokeWithJavaException() throws Exception { + // temporary enable native java generic serialize + System.setProperty(ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE, "true"); Assertions.assertThrows(RpcException.class, () -> { Method genericInvoke = GenericService.class.getMethods()[0]; @@ -95,6 +98,7 @@ public class GenericFilterTest { genericFilter.invoke(invoker, invocation); }); + System.clearProperty(ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE); } @Test