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

kenhuuu pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/3.8-dev by this push:
     new 696d8636c4 Restrict GraphSON2/3 traversal strategy deserialization
696d8636c4 is described below

commit 696d8636c4c455ac423c56aac3a64fc2b3f3be4f
Author: Ken Hu <[email protected]>
AuthorDate: Thu Aug 20 19:32:22 2026 -0700

    Restrict GraphSON2/3 traversal strategy deserialization
    
    Assisted-by: Codex:gpt-5.6-sol
---
 CHANGELOG.asciidoc                                 |   1 +
 docs/src/reference/gremlin-applications.asciidoc   |  16 ++-
 docs/src/upgrade/release-3.8.2.asciidoc            |  21 ++++
 .../io/graphson/TraversalSerializersV2.java        |   9 +-
 .../io/graphson/TraversalSerializersV3.java        |   9 +-
 .../graphson/GraphSONMapperEmbeddedTypeTest.java   |  35 ++++--
 ...versalStrategyProxyJacksonDeserializerTest.java | 127 +++++++++++++++++++++
 .../util/ser/GraphSONMessageSerializerV2Test.java  |  40 +++++++
 .../util/ser/GraphSONMessageSerializerV3Test.java  |  39 +++++++
 9 files changed, 274 insertions(+), 23 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9770ca3c2d..44f00f5b97 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -36,6 +36,7 @@ This release also includes changes from prior 3.7.x releases.
 * Added a `propertyMap()` helper to view an element's properties as a map 
keyed by property key, on the `Element` structure API in `gremlin-core` 
(inherited by `Vertex`, `Edge`, and `VertexProperty`) and on `Vertex`, `Edge`, 
and `VertexProperty` in `gremlin-javascript`, `gremlin-python`, 
`gremlin-dotnet`, and `gremlin-go`.
 * Fixed a bug in `gremlin-go` where a `VertexProperty` deserialized from 
GraphBinary did not have its `Key` field populated, causing 
`Vertex.PropertyMap()` to group properties under an empty key.
 * Removed `final` from `RepeatStep` to allow providers to extend it.
+* Restricted GraphSON 2.0 and 3.0 `TraversalStrategy` deserialization to 
registered strategies.
 
 [[release-3-8-1]]
 === TinkerPop 3.8.1 (Release Date: April 1, 2026)
diff --git a/docs/src/reference/gremlin-applications.asciidoc 
b/docs/src/reference/gremlin-applications.asciidoc
index 36a9b0a048..dbf46214e0 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -1218,6 +1218,21 @@ Gremlin Server is configured by default with GraphSON 
3.0 as shown above. It has
 |ioRegistries |A list of `IoRegistry` implementations to be applied to the 
serializer. |_none_
 |=========================================================
 
+The GraphSON 2.0 and 3.0 reference implementations only deserialize a 
`TraversalStrategy` when its class is registered
+with `TraversalStrategies.GlobalCache`. TinkerPop's built-in strategies are 
registered by default. Providers must
+register every custom strategy before GraphSON input is read, either as part 
of a graph or graph computer strategy set
+with `registerStrategies()`, or individually with `registerStrategy()`:
+
+[source,java]
+----
+TraversalStrategies.GlobalCache.registerStrategies(MyGraph.class, 
traversalStrategies);
+// or
+TraversalStrategies.GlobalCache.registerStrategy(MyStrategy.class);
+----
+
+The presence of a strategy on the application class path is not sufficient. 
Registration permits serialized data to
+construct the strategy from its configuration, so all strategies should be 
registered.
+
 It is worth noting that GraphSON 1.0 still has some appeal for some users as 
it can be configured to produce an untyped
 JSON format which is a bit easier to consume than its successors which embed 
data types into the output. This version
 of GraphSON tends to be the one that users like to utilize when 
<<connecting-via-http,connecting via HTTP>> and is still
@@ -3196,4 +3211,3 @@ create large or frequently changing value sets, improving 
performance and stabil
 
 Consult the MCP client documentation for how environment variables are 
supplied and how tool calls are approved and
 presented to the user.
-
diff --git a/docs/src/upgrade/release-3.8.2.asciidoc 
b/docs/src/upgrade/release-3.8.2.asciidoc
index 2b8ffbd336..61330de328 100644
--- a/docs/src/upgrade/release-3.8.2.asciidoc
+++ b/docs/src/upgrade/release-3.8.2.asciidoc
@@ -77,3 +77,24 @@ Map<String, List<Property<Object>>> propertyMap = 
vertex.propertyMap();
 ----
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-3187[TINKERPOP-3187]
+
+=== Upgrading for Providers
+
+==== Graph System Providers
+
+===== GraphSON Strategy Registry
+
+Before 3.8.2, GraphSON 2.0 and 3.0 deserialization loaded a custom 
`TraversalStrategy` from the application class path
+based on its serialized class name. Starting with 3.8.2, GraphSON only 
deserializes strategies registered with
+`TraversalStrategies.GlobalCache`.
+
+Providers that send custom strategies over GraphSON must register every 
strategy before input is deserialized. Either
+`registerStrategies()` or `registerStrategy()` can be used:
+
+[source,java]
+----
+TraversalStrategies.GlobalCache.registerStrategies(MyGraph.class, 
traversalStrategies);
+// or
+TraversalStrategies.GlobalCache.registerStrategy(MyStrategy.class);
+----
+
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
index 218b6fd94f..7035ec8105 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.NotP;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
@@ -544,11 +545,9 @@ final class TraversalSerializersV2 {
             final Class clasz;
             final Map<String,Object> mapConf = (Map<String,Object>) 
data.get("conf");
             if (null == this.clazz || this.clazz == 
TraversalStrategyProxy.class) {
-                try {
-                    clasz = Class.forName(data.get("fqcn").toString());
-                } catch (Exception ex) {
-                    throw new IllegalArgumentException("Could not load class " 
+ mapConf.get("fqcn").toString(), ex);
-                }
+                final String className = data.get("fqcn").toString();
+                clasz = 
TraversalStrategies.GlobalCache.getRegisteredStrategyClassByFullName(className).
+                        orElseThrow(() -> new 
IllegalArgumentException("TraversalStrategy not recognized - " + className));
             } else {
                 clasz = this.clazz;
             }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
index ab5d47a230..ec98331826 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
@@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.NotP;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
@@ -637,11 +638,9 @@ final class TraversalSerializersV3 {
             final Class clasz;
             final Map<String,Object> mapConf = (Map<String,Object>) 
data.get("conf");
             if (null == this.clazz || this.clazz == 
TraversalStrategyProxy.class) {
-                try {
-                    clasz = Class.forName(data.get("fqcn").toString());
-                } catch (Exception ex) {
-                    throw new IllegalArgumentException("Could not load class " 
+ mapConf.get("fqcn").toString(), ex);
-                }
+                final String className = data.get("fqcn").toString();
+                clasz = 
TraversalStrategies.GlobalCache.getRegisteredStrategyClassByFullName(className).
+                        orElseThrow(() -> new 
IllegalArgumentException("TraversalStrategy not recognized - " + className));
             } else {
                 clasz = this.clazz;
             }
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
index 510df3d613..6bf345996a 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Compare;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.PBiPredicate;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep;
@@ -512,24 +513,34 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
     public void shouldHandleTraversalStrategyProxy() throws Exception {
         assumeThat(version,  either(startsWith("v2")).or(startsWith("v3")));
 
-        final TraversalStrategyProxy o = new 
TraversalStrategyProxy(DummyTraversalStrategy.instance());
-        final TraversalStrategyProxy strategyProxy = 
serializeDeserialize(mapper, o, TraversalStrategyProxy.class);
-        assertThat(strategyProxy, instanceOf(TraversalStrategyProxy.class));
-        assertEquals(DummyTraversalStrategy.class, 
strategyProxy.getStrategyClass());
-        
DummyTraversalStrategy.instance().getConfiguration().getKeys().forEachRemaining(
-                k -> 
assertEquals(DummyTraversalStrategy.instance().getConfiguration().getProperty(k),
 strategyProxy.getConfiguration().getProperty(k)));
+        
TraversalStrategies.GlobalCache.registerStrategy(DummyTraversalStrategy.class);
+        try {
+            final TraversalStrategyProxy o = new 
TraversalStrategyProxy(DummyTraversalStrategy.instance());
+            final TraversalStrategyProxy strategyProxy = 
serializeDeserialize(mapper, o, TraversalStrategyProxy.class);
+            assertThat(strategyProxy, 
instanceOf(TraversalStrategyProxy.class));
+            assertEquals(DummyTraversalStrategy.class, 
strategyProxy.getStrategyClass());
+            
DummyTraversalStrategy.instance().getConfiguration().getKeys().forEachRemaining(
+                    k -> 
assertEquals(DummyTraversalStrategy.instance().getConfiguration().getProperty(k),
 strategyProxy.getConfiguration().getProperty(k)));
+        } finally {
+            
TraversalStrategies.GlobalCache.unregisterStrategy(DummyTraversalStrategy.class);
+        }
     }
 
     @Test
     public void shouldHandleTraversalStrategyProxyWithConfig() throws 
Exception {
         assumeThat(version,  either(startsWith("v2")).or(startsWith("v3")));
 
-        final TraversalStrategyProxy o = new 
TraversalStrategyProxy(DummyConfiguredTraversalStrategy.instance());
-        final TraversalStrategyProxy strategyProxy = 
serializeDeserialize(mapper, o, TraversalStrategyProxy.class);
-        assertThat(strategyProxy, instanceOf(TraversalStrategyProxy.class));
-        assertEquals(DummyConfiguredTraversalStrategy.class, 
strategyProxy.getStrategyClass());
-        
DummyConfiguredTraversalStrategy.instance().getConfiguration().getKeys().forEachRemaining(
-                k -> 
assertEquals(DummyConfiguredTraversalStrategy.instance().getConfiguration().getProperty(k),
 strategyProxy.getConfiguration().getProperty(k)));
+        
TraversalStrategies.GlobalCache.registerStrategy(DummyConfiguredTraversalStrategy.class);
+        try {
+            final TraversalStrategyProxy o = new 
TraversalStrategyProxy(DummyConfiguredTraversalStrategy.instance());
+            final TraversalStrategyProxy strategyProxy = 
serializeDeserialize(mapper, o, TraversalStrategyProxy.class);
+            assertThat(strategyProxy, 
instanceOf(TraversalStrategyProxy.class));
+            assertEquals(DummyConfiguredTraversalStrategy.class, 
strategyProxy.getStrategyClass());
+            
DummyConfiguredTraversalStrategy.instance().getConfiguration().getKeys().forEachRemaining(
+                    k -> 
assertEquals(DummyConfiguredTraversalStrategy.instance().getConfiguration().getProperty(k),
 strategyProxy.getConfiguration().getProperty(k)));
+        } finally {
+            
TraversalStrategies.GlobalCache.unregisterStrategy(DummyConfiguredTraversalStrategy.class);
+        }
     }
 
     @Test
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalStrategyProxyJacksonDeserializerTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalStrategyProxyJacksonDeserializerTest.java
new file mode 100644
index 0000000000..bfef5f78cb
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalStrategyProxyJacksonDeserializerTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.tinkerpop.gremlin.structure.io.graphson;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class TraversalStrategyProxyJacksonDeserializerTest {
+
+    private static boolean loadRecordingStrategyInitialized;
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {"v2", GraphSONMapper.build().version(GraphSONVersion.V2_0).
+                        
typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()},
+                {"v3", GraphSONMapper.build().version(GraphSONVersion.V3_0).
+                        
typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()}
+        });
+    }
+
+    @Parameterized.Parameter(0)
+    public String version;
+
+    @Parameterized.Parameter(1)
+    public ObjectMapper mapper;
+
+    @After
+    public void unregisterStrategy() {
+        
TraversalStrategies.GlobalCache.unregisterStrategy(LoadRecordingStrategy.class);
+    }
+
+    @Test
+    public void shouldRejectStrategyThatIsNotRegistered() throws Exception {
+        final String fqcn = LoadRecordingStrategy.class.getName();
+        try {
+            readStrategy(fqcn);
+            fail("A strategy that is not registered must not deserialize");
+        } catch (IOException ex) {
+            assertThat(ex.getMessage(), containsString("TraversalStrategy not 
recognized - " + fqcn));
+        }
+    }
+
+    @Test
+    public void shouldRejectStrategyThatIsNotRegisteredWithoutInitializingIt() 
throws Exception {
+        final String fqcn = LoadRecordingStrategy.class.getName();
+        try {
+            readStrategy(fqcn);
+            fail("A strategy that is not registered must not deserialize");
+        } catch (IOException ignored) {
+            // asserted on by shouldRejectStrategyThatIsNotRegistered
+        }
+
+        assertFalse("The rejected strategy was initialized, so the check ran 
after the class was loaded",
+                loadRecordingStrategyInitialized);
+    }
+
+    @Test
+    public void shouldAdmitStrategyRegisteredAsABuiltIn() throws Exception {
+        assertEquals(SubgraphStrategy.class,
+                
readStrategy(SubgraphStrategy.class.getName()).getStrategyClass());
+    }
+
+    @Test
+    public void shouldAdmitStrategyRegisteredByAProvider() throws Exception {
+        
TraversalStrategies.GlobalCache.registerStrategy(LoadRecordingStrategy.class);
+
+        assertEquals(LoadRecordingStrategy.class,
+                
readStrategy(LoadRecordingStrategy.class.getName()).getStrategyClass());
+    }
+
+    private TraversalStrategyProxy readStrategy(final String fqcn) throws 
IOException {
+        return mapper.readValue(
+                
String.format("{\"@type\":\"g:TraversalStrategy\",\"@value\":{\"fqcn\":\"%s\",\"conf\":{}}}",
+                        fqcn),
+                TraversalStrategyProxy.class);
+    }
+
+    private static final class LoadRecordingStrategy
+            extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+            implements TraversalStrategy.DecorationStrategy {
+
+        static {
+            loadRecordingStrategyInitialized = true;
+        }
+
+        @Override
+        public void apply(final Traversal.Admin<?, ?> traversal) {
+            // do nothing
+        }
+    }
+}
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV2Test.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV2Test.java
index bb5f77bd18..49c20b43a9 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV2Test.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV2Test.java
@@ -21,12 +21,17 @@ package org.apache.tinkerpop.gremlin.util.ser;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.commons.configuration2.BaseConfiguration;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
 import org.apache.tinkerpop.gremlin.util.MessageSerializer;
 import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.util.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.util.message.ResponseStatusCode;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Property;
@@ -70,7 +75,9 @@ import java.util.UUID;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -85,6 +92,7 @@ import static org.junit.Assert.fail;
 public class GraphSONMessageSerializerV2Test {
 
     public static final GraphSONMessageSerializerV2 SERIALIZER = new 
GraphSONMessageSerializerV2();
+    private static boolean unregisteredStrategyInitialized;
     private static final RequestMessage msg = RequestMessage.build("op")
             
.overrideRequestId(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595")).create();
     private static final ObjectMapper mapper = new ObjectMapper();
@@ -423,6 +431,24 @@ public class GraphSONMessageSerializerV2Test {
         assertEquals(ResponseStatusCode.SUCCESS.getValue(), 
deserialized.getStatus().getCode().getValue());
         assertEquals("worked", deserialized.getStatus().getMessage());
     }
+
+    @Test
+    public void shouldRejectUnregisteredTraversalStrategyInResponse() throws 
Exception {
+        final String fqcn = UnregisteredStrategy.class.getName();
+        final TraversalStrategyProxy<UnregisteredStrategy> strategy =
+                new TraversalStrategyProxy<>(UnregisteredStrategy.class, new 
BaseConfiguration());
+        final String response = SERIALIZER.serializeResponseAsString(
+                
ResponseMessage.build(UUID.randomUUID()).result(strategy).create(), allocator);
+
+        try {
+            SERIALIZER.deserializeResponse(response);
+            fail("An unregistered strategy in a server response must not 
deserialize");
+        } catch (SerializationException ex) {
+            assertThat(ex.getMessage(), containsString("TraversalStrategy not 
recognized - " + fqcn));
+        }
+
+        assertFalse("The rejected strategy was initialized", 
unregisteredStrategyInitialized);
+    }
     
     @Test
     public void shouldSerializeToTreeJson() throws Exception {
@@ -589,6 +615,20 @@ public class GraphSONMessageSerializerV2Test {
         }
     }
 
+    private static final class UnregisteredStrategy
+            extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+            implements TraversalStrategy.DecorationStrategy {
+
+        static {
+            unregisteredStrategyInitialized = true;
+        }
+
+        @Override
+        public void apply(final Traversal.Admin<?, ?> traversal) {
+            // do nothing
+        }
+    }
+
     public static class ColorIoRegistry extends AbstractIoRegistry {
         public ColorIoRegistry() {
             register(GraphSONIo.class, null, new ColorSimpleModule());
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV3Test.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV3Test.java
index 3a9c0e537d..440343ea84 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV3Test.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV3Test.java
@@ -21,7 +21,11 @@ package org.apache.tinkerpop.gremlin.util.ser;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.commons.configuration2.BaseConfiguration;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.apache.tinkerpop.gremlin.util.MessageSerializer;
 import org.apache.tinkerpop.gremlin.util.Tokens;
@@ -55,7 +59,9 @@ import java.util.UUID;
 import static 
org.apache.tinkerpop.gremlin.util.MockitoHamcrestMatcherAdapter.reflectionEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasItemInArray;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -69,6 +75,7 @@ import static org.junit.Assert.fail;
 @SuppressWarnings("unchecked")
 public class GraphSONMessageSerializerV3Test {
 
+    private static boolean unregisteredStrategyInitialized;
     private final UUID requestId = 
UUID.fromString("6457272A-4018-4538-B9AE-08DD5DDC0AA1");
     private final ResponseMessage.Builder responseMessageBuilder = 
ResponseMessage.build(requestId);
     private final static ByteBufAllocator allocator = 
UnpooledByteBufAllocator.DEFAULT;
@@ -342,6 +349,24 @@ public class GraphSONMessageSerializerV3Test {
         assertEquals("worked", deserialized.getStatus().getMessage());
     }
 
+    @Test
+    public void shouldRejectUnregisteredTraversalStrategyInResponse() throws 
Exception {
+        final String fqcn = UnregisteredStrategy.class.getName();
+        final TraversalStrategyProxy<UnregisteredStrategy> strategy =
+                new TraversalStrategyProxy<>(UnregisteredStrategy.class, new 
BaseConfiguration());
+        final String response = serializer.serializeResponseAsString(
+                
ResponseMessage.build(UUID.randomUUID()).result(strategy).create(), allocator);
+
+        try {
+            serializer.deserializeResponse(response);
+            fail("An unregistered strategy in a server response must not 
deserialize");
+        } catch (SerializationException ex) {
+            assertThat(ex.getMessage(), containsString("TraversalStrategy not 
recognized - " + fqcn));
+        }
+
+        assertFalse("The rejected strategy was initialized", 
unregisteredStrategyInitialized);
+    }
+
     @Test
     public void shouldDeserializeNotPredicate() throws Exception {
         final String requestMessageWithP = 
"{\"requestId\":{\"@type\":\"g:UUID\",\"@value\":\"0397b9c0-ffab-470e-a6a8-644fc80c01d6\"},\"op\":\"bytecode\",\"processor\":\"traversal\",\"args\":{\"gremlin\":{\"@type\":\"g:Bytecode\",\"@value\":{\"step\":[[\"V\"],[\"hasLabel\",\"person\"],[\"has\",\"age\",{\"@type\":\"g:P\",\"@value\":{\"predicate\":\"not\",\"value\":{\"@type\":\"g:P\",\"@value\":{\"predicate\":\"lte\",\"value\":{\"@type\":\"g:Int32\",\"@value\":10}}}}}]]}},\"aliases\":{\"g\
 [...]
@@ -416,4 +441,18 @@ public class GraphSONMessageSerializerV3Test {
     private ResponseMessage convert(final Object toSerialize) throws 
SerializationException {
         return convert(toSerialize, this.serializer);
     }
+
+    private static final class UnregisteredStrategy
+            extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+            implements TraversalStrategy.DecorationStrategy {
+
+        static {
+            unregisteredStrategyInitialized = true;
+        }
+
+        @Override
+        public void apply(final Traversal.Admin<?, ?> traversal) {
+            // do nothing
+        }
+    }
 }

Reply via email to