You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2020/05/12 14:44:07 UTC

[ignite] branch master updated: IGNITE-12962 Add support of classes whitelist and blacklist for HTTP-REST - Fixes #7773.

This is an automated email from the ASF dual-hosted git repository.

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 29ef5c8  IGNITE-12962 Add support of classes whitelist and blacklist for HTTP-REST - Fixes #7773.
29ef5c8 is described below

commit 29ef5c819e605b1ddfe7716b0645a25df0c08d9e
Author: Pavel Pereslegin <xx...@gmail.com>
AuthorDate: Tue May 12 17:09:40 2020 +0300

    IGNITE-12962 Add support of classes whitelist and blacklist for HTTP-REST - Fixes #7773.
    
    Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
 .../rest/JettyRestProcessorAbstractSelfTest.java   | 74 ++++++++++++++++++++--
 .../test/config/class_list_exploit_included.txt    |  3 +-
 .../http/jetty/GridJettyObjectMapper.java          | 35 +++++++++-
 3 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
index 2a0aca1..2443eba 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java
@@ -151,6 +151,7 @@ import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.Test;
 
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST;
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
 import static org.apache.ignite.cache.CacheMode.REPLICATED;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC;
@@ -175,12 +176,22 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
 
     /** {@inheritDoc} */
     @Override protected void beforeTestsStarted() throws Exception {
+        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
+        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
+
         super.beforeTestsStarted();
 
         initCache();
     }
 
     /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        System.clearProperty(IGNITE_MARSHALLER_BLACKLIST);
+
+        super.afterTestsStopped();
+    }
+
+    /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
         grid(0).cache(DEFAULT_CACHE_NAME).removeAll();
 
@@ -655,6 +666,23 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
         );
 
         assertResponseContainsError(ret, "Failed to convert value to specified type");
+
+        // Check forbidden type.
+        ForbiddenType forbidden = new ForbiddenType(new Exploit[] {
+            new Exploit(1),
+            new Exploit(2)
+        });
+
+        json = JSON_MAPPER.writeValueAsString(forbidden);
+
+        ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_PUT,
+            "keyType", "int",
+            "key", "5",
+            "valueType", ForbiddenType.class.getName(),
+            "val", json
+        );
+
+        assertResponseContainsError(ret, "Deserialization of class " + Exploit.class.getName() + " is disallowed.");
     }
 
     /**
@@ -3280,8 +3308,46 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
     }
 
     /** */
+    private static class ForbiddenType {
+        /** Data. */
+        @JsonProperty
+        private Exploit[] data;
+
+        /** */
+        ForbiddenType() {
+            // No-op.
+        }
+
+        /**
+         * @param data Data.
+         */
+        ForbiddenType(Exploit[] data) {
+            this.data = data;
+        }
+    }
+
+    /** */
+    private static class Exploit {
+        /** Value. */
+        @JsonProperty
+        private int val = 10;
+
+        /**
+         * @param val Value
+         */
+        Exploit(int val) {
+            this.val = val;
+        }
+
+        /** */
+        Exploit() {
+            // No-op.
+        }
+    }
+
+    /** */
     @JsonInclude(JsonInclude.Include.NON_NULL)
-    public static class OuterClass {
+    private static class OuterClass {
         /** */
         @JsonProperty
         private long id;
@@ -3329,7 +3395,6 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
         }
 
         /** */
-        @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
         OuterClass(long id, String name, double doubleVal, List<Integer> list, Timestamp timestamp, long[] longs,
             UUID uuid, IgniteUuid igniteUuid, OptionalObject optional) {
             this.id = id;
@@ -3412,7 +3477,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
     }
 
     /** */
-    public enum Color {
+    private enum Color {
         /** */
         RED,
 
@@ -3424,8 +3489,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends JettyRestProces
     }
 
     /** Complex entity. */
-    @SuppressWarnings({"InnerClassMayBeStatic", "AssignmentOrReturnOfFieldWithMutableType"})
-    static class Complex implements Serializable {
+    private static class Complex implements Serializable {
         /** */
         @QuerySqlField(index = true)
         @JsonProperty
diff --git a/modules/core/src/test/config/class_list_exploit_included.txt b/modules/core/src/test/config/class_list_exploit_included.txt
index b8cb550..b461d53 100644
--- a/modules/core/src/test/config/class_list_exploit_included.txt
+++ b/modules/core/src/test/config/class_list_exploit_included.txt
@@ -17,4 +17,5 @@
 
 org.apache.ignite.spi.discovery.tcp.DiscoveryUnmarshalVulnerabilityTest$Exploit
 org.apache.ignite.stream.socket.SocketStreamerUnmarshalVulnerabilityTest$Exploit
-org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest$Exploit
\ No newline at end of file
+org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest$Exploit
+org.apache.ignite.internal.processors.rest.JettyRestProcessorAbstractSelfTest$Exploit
\ No newline at end of file
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
index 28c4298..98c214f 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyObjectMapper.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.rest.protocols.http.jetty;
 
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -39,6 +40,7 @@ import java.sql.SQLException;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.util.Locale;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.binary.BinaryObjectException;
 import org.apache.ignite.binary.BinaryType;
@@ -50,6 +52,7 @@ import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.visor.util.VisorExceptionWrapper;
 import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteUuid;
 
 /**
@@ -88,9 +91,14 @@ public class GridJettyObjectMapper extends ObjectMapper {
         module.addSerializer(Date.class, IGNITE_SQLDATE_SERIALIZER);
         module.addDeserializer(Date.class, IGNITE_SQLDATE_DESERIALIZER);
 
-        if (ctx != null)
+        if (ctx != null) {
             module.addDeserializer(BinaryObject.class, new IgniteBinaryObjectJsonDeserializer(ctx));
 
+            IgnitePredicate<String> clsFilter = ctx.marshallerContext().classNameFilter();
+
+            setDefaultTyping(new RestrictedTypeResolverBuilder(clsFilter).init(JsonTypeInfo.Id.CLASS, null));
+        }
+
         configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
         configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 
@@ -329,4 +337,29 @@ public class GridJettyObjectMapper extends ObjectMapper {
             return Date.valueOf(parser.getText());
         }
     };
+
+    /** */
+    private static class RestrictedTypeResolverBuilder extends DefaultTypeResolverBuilder {
+        /** */
+        private final IgnitePredicate<String> clsFilter;
+
+        /**
+         * @param clsFilter Class name filter.
+         */
+        public RestrictedTypeResolverBuilder(IgnitePredicate<String> clsFilter) {
+            super(DefaultTyping.NON_FINAL);
+
+            this.clsFilter = clsFilter;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean useForType(JavaType type) {
+            String clsName = type.getRawClass().getName();
+
+            if (!clsFilter.apply(clsName))
+                throw new IgniteException("Deserialization of class " + clsName + " is disallowed.");
+
+            return false;
+        }
+    }
 }