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

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

commit 0942114b3c43698606739bfb04f3317a67275cb5
Author: Ken Hu <[email protected]>
AuthorDate: Thu Aug 6 09:29:18 2026 -0700

    Restrict TraversalStrategy deserialization to allowed classes
    
    Add explicit allow-lists for GraphSON 2/3 and GraphBinary, enforce
    strategy validation in JavaTranslator, and merge provider allow-lists
    with custom GraphBinary registry builders. This prevents loading and
    running classes unintentionally.
    
    Assisted-by: Codex:gpt-5.5
---
 CHANGELOG.asciidoc                                 |   1 +
 docs/src/reference/gremlin-applications.asciidoc   |  19 +-
 docs/src/upgrade/release-3.8.2.asciidoc            |  57 +++++
 .../tinkerpop/gremlin/jsr223/JavaTranslator.java   |   4 +
 .../strategy/TraversalStrategyResolver.java        | 258 +++++++++++++++++++++
 .../io/binary/TypeSerializerRegistry.java          |  39 +++-
 .../binary/types/TraversalStrategySerializer.java  |  28 ++-
 .../structure/io/graphson/GraphSONMapper.java      |  17 +-
 .../structure/io/graphson/GraphSONModule.java      |  44 +++-
 .../io/graphson/TraversalSerializersV2.java        |  22 +-
 .../io/graphson/TraversalSerializersV3.java        |  22 +-
 .../gremlin/jsr223/JavaTranslatorTest.java         |  33 ++-
 .../strategy/TestTraversalStrategies.java          |  79 +++++++
 .../graphson/GraphSONMapperEmbeddedTypeTest.java   |  64 ++---
 .../ser/AbstractGraphSONMessageSerializerV2.java   |   1 +
 .../util/ser/AbstractMessageSerializer.java        |  41 +++-
 .../util/ser/GraphBinaryMessageSerializerV1.java   |   1 +
 .../gremlin/util/TestTraversalStrategies.java      |  46 ++++
 .../util/ser/GraphSONMessageSerializerV3Test.java  |  37 ++-
 .../binary/GraphBinaryMessageSerializerV1Test.java |  51 +++-
 .../types/TraversalStrategySerializerTest.java     |  91 ++++++++
 21 files changed, 876 insertions(+), 79 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 140c3d057f..675bae3077 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@ This release also includes changes from prior 3.7.x releases.
 * Bumped to Groovy 4.0.32 which adds support for parsing Java 25 bytecode.
 * Bumped Hadoop to 3.4.3 (and Kerby to 2.0.3) to enable `hadoop-gremlin` to 
build and run on Java 25.
 * Add missing `Configuring` interface to `GraphStepPlaceholder` and 
`VertexStepPlaceholder`
+* Restricted typed GraphSON and GraphBinary `TraversalStrategy` 
deserialization to TinkerPop strategies and explicitly allowed provider 
strategies.
 * Fixed bug in `group()` value traversal where keys were retained with stale 
barrier state instead of being filtered when steps following a `Barrier` in the 
second `by()` produced no output (e.g. `by(values("age").fold().unfold())` or 
`by(__.out().fold().count(local).is(P.gt(0)))` for vertices with no out-edges).
 * Fixed bug in `gremlin-javascript` GraphBinary and GraphSON deserialization 
where `OffsetDateTime` values outside the JavaScript `Date` range were silently 
returned as invalid `Date` objects instead of failing deserialization.
 * 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`.
diff --git a/docs/src/reference/gremlin-applications.asciidoc 
b/docs/src/reference/gremlin-applications.asciidoc
index 77e71d3917..ff47f8ff9b 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -1216,8 +1216,15 @@ Gremlin Server is configured by default with GraphSON 
3.0 as shown above. It has
 |=========================================================
 |Key |Description |Default
 |ioRegistries |A list of `IoRegistry` implementations to be applied to the 
serializer. |_none_
+|allowedTraversalStrategies |A list of fully qualified `TraversalStrategy` 
class names that may be deserialized from typed GraphSON request or response 
data, in addition to TinkerPop's built-in strategies. |_none_
 |=========================================================
 
+Typed GraphSON accepts serialized `TraversalStrategy` values only for 
TinkerPop's built-in strategies and classes named
+in `allowedTraversalStrategies`. This setting applies to custom provider 
strategies that cross a remote serialization
+boundary in request or response data. A custom strategy that is not listed is 
rejected during deserialization. The same
+allow-list is available in programmatic configuration with
+`GraphSONMapper.Builder.addAllowedTraversalStrategy(Class<? extends 
TraversalStrategy>)`.
+
 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
@@ -1271,8 +1278,19 @@ It has the MIME type of 
`application/vnd.graphbinary-v1.0` and the following con
 |custom |A list of classes with custom kryo `Serializer` implementations 
related to them in the form of `<class>;<serializer-class>`. |_none_
 |ioRegistries |A list of `IoRegistry` implementations to be applied to the 
serializer. |_none_
 |builder |Name of the `TypeSerializerRegistry.Builder` instance to be used to 
construct the `TypeSerializerRegistry`. |_none_
+|allowedTraversalStrategies |A list of fully qualified `TraversalStrategy` 
class names that may be deserialized from GraphBinary request or response data, 
in addition to TinkerPop's built-in strategies. |_none_
 |=========================================================
 
+GraphBinary accepts serialized `TraversalStrategy` values only for TinkerPop's 
built-in strategies and classes named in
+`allowedTraversalStrategies`. This setting applies to custom provider 
strategies that cross a remote serialization
+boundary in request or response data. A custom strategy that is not listed is 
rejected during deserialization. The same
+allow-list is available in programmatic configuration with
+`TypeSerializerRegistry.Builder.addAllowedTraversalStrategy(Class<? extends 
TraversalStrategy>)`.
+
+When a custom `builder` pre-registers the standard 
`TraversalStrategySerializer`, entries from
+`allowedTraversalStrategies` are merged into that serializer's allow-list. If 
the custom builder registers a different
+serializer for `TraversalStrategy`, configuration fails because the allow-list 
cannot be safely applied.
+
 As described above, there are multiple ways in which to register serializers 
for GraphBinary-based serialization. Note
 that the `ioRegistries` setting is applied first, followed by the `custom` 
setting.
 
@@ -3179,4 +3197,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..df5117f3bb 100644
--- a/docs/src/upgrade/release-3.8.2.asciidoc
+++ b/docs/src/upgrade/release-3.8.2.asciidoc
@@ -77,3 +77,60 @@ Map<String, List<Property<Object>>> propertyMap = 
vertex.propertyMap();
 ----
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-3187[TINKERPOP-3187]
+
+==== TraversalStrategy Deserialization Allow-List
+
+Typed GraphSON 2.0, typed GraphSON 3.0 and GraphBinary now restrict 
`TraversalStrategy` deserialization to built-in
+TinkerPop strategies and strategies that have been explicitly allowed. This 
change prevents untrusted request or
+response data from naming arbitrary JVM classes in a serialized 
`g:TraversalStrategy`.
+
+Users who send or receive custom `TraversalStrategy` implementations over 
these formats must add those strategy classes
+to the serializer configuration. If a custom strategy is not registered, 
deserialization will fail with an error
+indicating that the `TraversalStrategy` class is not allowed.
+
+For embedded or programmatic serializer configuration, add the strategies to 
the relevant mapper or registry builder:
+
+[source,text]
+----
+GraphSONMapper graphSONMapper = GraphSONMapper.build().
+        addAllowedTraversalStrategy(MyStrategy.class).
+        create();
+
+TypeSerializerRegistry graphBinaryRegistry = TypeSerializerRegistry.build().
+        addAllowedTraversalStrategy(MyStrategy.class).
+        create();
+----
+
+Only register strategy classes that are intended to be loadable from remote 
data and are safe to construct from their
+serialized configuration.
+
+=== Upgrading for Providers
+
+==== Graph System Providers
+
+===== TraversalStrategy Deserialization Allow-List
+
+Gremlin Server based remote providers that define custom `TraversalStrategy` 
classes and expect them to cross the
+client/server boundary must register those classes with the serializers used 
by their clients and servers. Built-in
+TinkerPop strategies remain available by default, but custom strategies are no 
longer deserialized solely because
+their fully qualified class name appears in `g:TraversalStrategy` data.
+
+Providers that package Gremlin Server configuration should add their custom 
strategy classes to the serializer
+configuration with `allowedTraversalStrategies`.
+
+[source,text]
+----
+serializers:
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1,
+      config: { allowedTraversalStrategies: [com.example.MyStrategy] }}
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3,
+      config: { allowedTraversalStrategies: [com.example.MyStrategy] }}
+----
+
+Providers that construct serializers programmatically should call 
`GraphSONMapper.Builder.addAllowedTraversalStrategy()`
+for GraphSON and 
`TypeSerializerRegistry.Builder.addAllowedTraversalStrategy()` for GraphBinary 
before creating the
+mapper or registry.
+
+For GraphBinary, configured strategies are merged with a standard 
`TraversalStrategySerializer` registered by a custom
+`TypeSerializerRegistry.Builder`. A custom builder that registers a different 
serializer for `TraversalStrategy` cannot
+also use `allowedTraversalStrategies`, because TinkerPop cannot safely apply 
the allow-list to that serializer.
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
index baca8dc8c3..f4dd62c276 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
@@ -31,6 +31,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTra
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -184,6 +185,9 @@ public final class JavaTranslator<S extends 
TraversalSource, T extends Traversal
 
     private Object invokeStrategyCreationMethod(final Object delegate, final 
Map<String, Object> map) {
         final Class<?> strategyClass = ((TraversalStrategyProxy) 
delegate).getStrategyClass();
+        if (!TraversalStrategyResolver.isGloballyAllowed(strategyClass))
+            throw new IllegalStateException(String.format("TraversalStrategy 
class is not allowed: %s", strategyClass.getName()));
+
         final Map<String, Method> methodCache = 
localMethodCache.computeIfAbsent(strategyClass, k -> {
             final Map<String, Method> cacheEntry = new HashMap<>();
             try {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyResolver.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyResolver.java
new file mode 100644
index 0000000000..2096a97587
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyResolver.java
@@ -0,0 +1,258 @@
+/*
+ * 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.process.traversal.strategy;
+
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.finalization.ComputerFinalizationStrategy;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.MessagePassingReductionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verification.VertexProgramRestrictionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SackStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SideEffectStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ReferenceElementStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ByModulatorOptimizationStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.GValueReductionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.LazyBarrierStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.provider.ProviderGValueReductionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReservedKeysVerificationStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Resolves traversal strategy class names supplied by typed GraphSON and 
GraphBinary payloads against an explicit
+ * registry. Deserializers use this class instead of direct class loading so 
that an incoming {@code fqcn} can only
+ * name a strategy that is part of TinkerPop's built-in set or one that a 
provider added to the resolver.
+ * <p>
+ * There are two validation points for traversal strategy proxies. The 
serializer-specific resolver validates the
+ * strategy name while bytes are being decoded. {@code JavaTranslator} later 
validates the resulting
+ * {@link TraversalStrategyProxy} before invoking {@code instance()} or {@code 
create(Configuration)} on the strategy
+ * class. The second check cannot refer back to the serializer instance that 
produced the proxy, so created resolvers
+ * also contribute their allowed classes to a process-wide set used by {@code 
JavaTranslator}.
+ */
+public final class TraversalStrategyResolver {
+
+    private static final Collection<Class<? extends TraversalStrategy>> 
DEFAULT_STRATEGIES = Collections.unmodifiableList(Arrays.asList(
+            ConnectiveStrategy.class,
+            ElementIdStrategy.class,
+            EventStrategy.class,
+            HaltedTraverserStrategy.class,
+            OptionsStrategy.class,
+            PartitionStrategy.class,
+            RequirementsStrategy.class,
+            SackStrategy.class,
+            SubgraphStrategy.class,
+            SeedStrategy.class,
+            SideEffectStrategy.class,
+            LazyBarrierStrategy.class,
+            MatchAlgorithmStrategy.class,
+            AdjacentToIncidentStrategy.class,
+            ByModulatorOptimizationStrategy.class,
+            ProductiveByStrategy.class,
+            CountStrategy.class,
+            GValueReductionStrategy.class,
+            FilterRankingStrategy.class,
+            IdentityRemovalStrategy.class,
+            IncidentToAdjacentStrategy.class,
+            InlineFilterStrategy.class,
+            MatchPredicateStrategy.class,
+            OrderLimitStrategy.class,
+            PathProcessorStrategy.class,
+            PathRetractionStrategy.class,
+            RepeatUnrollStrategy.class,
+            ProviderGValueReductionStrategy.class,
+            ComputerVerificationStrategy.class,
+            LambdaRestrictionStrategy.class,
+            ReadOnlyStrategy.class,
+            StandardVerificationStrategy.class,
+            EarlyLimitStrategy.class,
+            EdgeLabelVerificationStrategy.class,
+            ReservedKeysVerificationStrategy.class,
+            ReferenceElementStrategy.class,
+            ComputerFinalizationStrategy.class,
+            MessagePassingReductionStrategy.class,
+            ProfileStrategy.class,
+            VertexProgramRestrictionStrategy.class,
+            GraphFilterStrategy.class,
+            VertexProgramStrategy.class,
+            RemoteStrategy.class
+    ));
+
+    /*
+     * The global set is the union of strategy classes allowed by resolvers 
created in this JVM. It is not used by
+     * deserializers, which validate against their own resolver instance. It 
exists for JavaTranslator, where bytecode
+     * already contains a TraversalStrategyProxy and there is no 
serializer-local resolver to consult before reflective
+     * strategy creation.
+     */
+    private static final Set<Class<? extends TraversalStrategy>> 
GLOBALLY_ALLOWED_STRATEGIES = ConcurrentHashMap.newKeySet();
+    private static final TraversalStrategyResolver DEFAULT_RESOLVER = 
build().create();
+
+    private final Map<String, Class<? extends TraversalStrategy>> 
strategiesByName;
+
+    private TraversalStrategyResolver(final Collection<Class<? extends 
TraversalStrategy>> allowedStrategies) {
+        final Map<String, Class<? extends TraversalStrategy>> strategies = new 
LinkedHashMap<>();
+        allowedStrategies.forEach(strategy -> 
strategies.put(strategy.getName(), strategy));
+        this.strategiesByName = Collections.unmodifiableMap(strategies);
+    }
+
+    /**
+     * Creates a builder initialized with the default TinkerPop traversal 
strategy classes.
+     */
+    public static Builder build() {
+        return new Builder();
+    }
+
+    /**
+     * Gets the resolver for TinkerPop's default traversal strategy classes.
+     */
+    public static TraversalStrategyResolver defaultResolver() {
+        return DEFAULT_RESOLVER;
+    }
+
+    /**
+     * Determines whether a traversal strategy class has been allowed by any 
resolver created in this JVM. This check is
+     * intended for code paths such as {@code JavaTranslator} that receive a 
{@link TraversalStrategyProxy} after
+     * deserialization has already completed.
+     */
+    public static boolean isGloballyAllowed(final Class<?> strategyClass) {
+        return GLOBALLY_ALLOWED_STRATEGIES.contains(strategyClass);
+    }
+
+    /**
+     * Resolves a fully qualified class name to an allowed traversal strategy 
class.
+     *
+     * @throws IllegalArgumentException if the class name is not present in 
this resolver
+     */
+    public Class<? extends TraversalStrategy> resolve(final String fqcn) {
+        final Class<? extends TraversalStrategy> strategyClass = 
strategiesByName.get(fqcn);
+        if (null == strategyClass)
+            throw new 
IllegalArgumentException(String.format("TraversalStrategy class is not allowed: 
%s", fqcn));
+
+        return strategyClass;
+    }
+
+    /**
+     * Determines whether the supplied class is allowed by this resolver.
+     */
+    public boolean isAllowed(final Class<?> strategyClass) {
+        return null != strategyClass && 
strategiesByName.get(strategyClass.getName()) == strategyClass;
+    }
+
+    /**
+     * Gets the traversal strategy classes allowed by this resolver.
+     */
+    public Collection<Class<? extends TraversalStrategy>> 
getAllowedStrategies() {
+        return strategiesByName.values();
+    }
+
+    private static Optional<Class<? extends TraversalStrategy>> 
tryLoadSparqlStrategy() {
+        try {
+            final Class<?> sparqlStrategy = Class.forName(
+                    
"org.apache.tinkerpop.gremlin.sparql.process.traversal.strategy.SparqlStrategy",
+                    false,
+                    TraversalStrategyResolver.class.getClassLoader());
+            if (TraversalStrategy.class.isAssignableFrom(sparqlStrategy))
+                return Optional.of((Class<? extends TraversalStrategy>) 
sparqlStrategy);
+        } catch (Exception ignored) {
+            // optional module
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * Builds a traversal strategy resolver. Providers can add custom 
traversal strategy classes here so that remote
+     * GraphSON and GraphBinary deserialization can resolve them.
+     */
+    public static final class Builder {
+        private final Map<String, Class<? extends TraversalStrategy>> 
allowedStrategies = new LinkedHashMap<>();
+
+        private Builder() {
+            DEFAULT_STRATEGIES.forEach(this::addAllowedTraversalStrategy);
+            
tryLoadSparqlStrategy().ifPresent(this::addAllowedTraversalStrategy);
+        }
+
+        /**
+         * Adds a traversal strategy class that can be resolved during 
deserialization.
+         */
+        public Builder addAllowedTraversalStrategy(final Class<? extends 
TraversalStrategy> strategyClass) {
+            if (null == strategyClass)
+                throw new IllegalArgumentException("The traversal strategy 
class cannot be null");
+
+            allowedStrategies.put(strategyClass.getName(), strategyClass);
+            return this;
+        }
+
+        /**
+         * Adds traversal strategy classes that can be resolved during 
deserialization.
+         */
+        public Builder addAllowedTraversalStrategies(final Collection<Class<? 
extends TraversalStrategy>> strategyClasses) {
+            strategyClasses.forEach(this::addAllowedTraversalStrategy);
+            return this;
+        }
+
+        /**
+         * Creates the resolver and records the allowed strategy classes for 
later validation by {@code JavaTranslator}.
+         * The resolver-local allow-list is used during deserialization. The 
recorded global allow-list is used later
+         * when bytecode translation reaches a {@link TraversalStrategyProxy} 
without access to the serializer-local
+         * resolver that created it.
+         */
+        public TraversalStrategyResolver create() {
+            GLOBALLY_ALLOWED_STRATEGIES.addAll(allowedStrategies.values());
+            return new TraversalStrategyResolver(allowedStrategies.values());
+        }
+    }
+}
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
index f885a70723..316c9ef759 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
@@ -33,6 +33,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Scope;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
@@ -189,7 +190,6 @@ public class TypeSerializerRegistry {
             new RegistryEntry<>(ByteBuffer.class, new ByteBufferSerializer()),
             new RegistryEntry<>(Short.class, 
SingleTypeSerializer.ShortSerializer),
             new RegistryEntry<>(Boolean.class, 
SingleTypeSerializer.BooleanSerializer),
-            new RegistryEntry<>(TraversalStrategy.class, new 
TraversalStrategySerializer()),
             new RegistryEntry<>(BulkSet.class, new BulkSetSerializer()),
             new RegistryEntry<>(Tree.class, new TreeSerializer()),
             new RegistryEntry<>(Metrics.class, new MetricsSerializer()),
@@ -224,6 +224,7 @@ public class TypeSerializerRegistry {
     public static class Builder {
         private final List<RegistryEntry> list = new LinkedList<>();
         private Function<Class<?>, TypeSerializer<?>> fallbackResolver;
+        private final List<Class<? extends TraversalStrategy>> 
allowedTraversalStrategies = new LinkedList<>();
 
         /**
          * Adds a serializer for a built-in type.
@@ -281,6 +282,14 @@ public class TypeSerializerRegistry {
             return this;
         }
 
+        /**
+         * Allow a provider supplied {@link TraversalStrategy} to be 
deserialized from GraphBinary.
+         */
+        public Builder addAllowedTraversalStrategy(final Class<? extends 
TraversalStrategy> strategyClass) {
+            this.allowedTraversalStrategies.add(strategyClass);
+            return this;
+        }
+
         /**
          * Add {@link CustomTypeSerializer} by way of an {@link IoRegistry}. 
The registry entries should be bound to
          * {@link GraphBinaryIo}.
@@ -300,7 +309,33 @@ public class TypeSerializerRegistry {
          * Creates a new {@link TypeSerializerRegistry} instance based on the 
serializers added.
          */
         public TypeSerializerRegistry create() {
-            return new TypeSerializerRegistry(list, fallbackResolver);
+            final List<RegistryEntry> entries = new LinkedList<>(list);
+            boolean hasTraversalStrategySerializer = false;
+            for (int ix = 0; ix < entries.size(); ix++) {
+                final RegistryEntry entry = entries.get(ix);
+                if (entry.getType() != TraversalStrategy.class)
+                    continue;
+
+                hasTraversalStrategySerializer = true;
+                if (allowedTraversalStrategies.isEmpty())
+                    continue;
+
+                if (!(entry.getTypeSerializer() instanceof 
TraversalStrategySerializer))
+                    throw new IllegalStateException("The allowed traversal 
strategies cannot be applied to the " +
+                            "custom TraversalStrategy serializer");
+
+                final TraversalStrategySerializer serializer = 
(TraversalStrategySerializer) entry.getTypeSerializer();
+                entries.set(ix, new RegistryEntry<>(TraversalStrategy.class,
+                        
serializer.withAllowedTraversalStrategies(allowedTraversalStrategies)));
+            }
+
+            if (!hasTraversalStrategySerializer) {
+                final TraversalStrategyResolver traversalStrategyResolver = 
TraversalStrategyResolver.build().
+                        
addAllowedTraversalStrategies(allowedTraversalStrategies).create();
+                entries.add(new RegistryEntry<>(TraversalStrategy.class, new 
TraversalStrategySerializer(traversalStrategyResolver)));
+            }
+
+            return new TypeSerializerRegistry(entries, fallbackResolver);
         }
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
index 7790b213ea..eaa7d73b98 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/TraversalStrategySerializer.java
@@ -26,9 +26,11 @@ import 
org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
 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.process.traversal.strategy.TraversalStrategyResolver;
 import org.apache.tinkerpop.gremlin.structure.io.Buffer;
 
 import java.io.IOException;
+import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -37,13 +39,32 @@ import java.util.Map;
  */
 public class TraversalStrategySerializer extends 
SimpleTypeSerializer<TraversalStrategy> {
 
+    private final TraversalStrategyResolver traversalStrategyResolver;
+
     public TraversalStrategySerializer() {
+        this(TraversalStrategyResolver.defaultResolver());
+    }
+
+    public TraversalStrategySerializer(final TraversalStrategyResolver 
traversalStrategyResolver) {
         super(DataType.TRAVERSALSTRATEGY);
+        this.traversalStrategyResolver = traversalStrategyResolver;
+    }
+
+    /**
+     * Creates a serializer that preserves this serializer's allowed 
strategies and adds the supplied strategies.
+     */
+    public TraversalStrategySerializer withAllowedTraversalStrategies(
+            final Collection<Class<? extends TraversalStrategy>> 
strategyClasses) {
+        final TraversalStrategyResolver resolver = 
TraversalStrategyResolver.build().
+                
addAllowedTraversalStrategies(traversalStrategyResolver.getAllowedStrategies()).
+                addAllowedTraversalStrategies(strategyClasses).create();
+        return new TraversalStrategySerializer(resolver);
     }
 
     @Override
     protected TraversalStrategy readValue(final Buffer buffer, final 
GraphBinaryReader context) throws IOException {
-        final Class<TraversalStrategy> clazz = context.readValue(buffer, 
Class.class, false);
+        final String strategyClassName = context.readValue(buffer, 
String.class, false);
+        final Class<? extends TraversalStrategy> clazz = 
traversalStrategyResolver.resolve(strategyClassName);
         final Map config = context.readValue(buffer, Map.class, false);
 
         return new TraversalStrategyProxy(clazz, new MapConfiguration(config));
@@ -51,7 +72,10 @@ public class TraversalStrategySerializer extends 
SimpleTypeSerializer<TraversalS
 
     @Override
     protected void writeValue(final TraversalStrategy value, final Buffer 
buffer, final GraphBinaryWriter context) throws IOException {
-        context.writeValue(value.getClass(), buffer, false);
+        final Class<? extends TraversalStrategy> strategyClass = value 
instanceof TraversalStrategyProxy ?
+                ((TraversalStrategyProxy) value).getStrategyClass() :
+                value.getClass();
+        context.writeValue(strategyClass.getName(), buffer, false);
         
context.writeValue(translateToBytecode(ConfigurationConverter.getMap(value.getConfiguration())),
 buffer, false);
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java
index cda357caae..b8991d2ffa 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java
@@ -18,6 +18,8 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.graphson;
 
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.Mapper;
@@ -71,6 +73,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> {
     private final GraphSONVersion version;
     private final TypeInfo typeInfo;
     private final StreamReadConstraints streamReadConstraints;
+    private final TraversalStrategyResolver traversalStrategyResolver;
 
     private GraphSONMapper(final Builder builder) {
         this.customModules = builder.customModules;
@@ -79,6 +82,8 @@ public class GraphSONMapper implements Mapper<ObjectMapper> {
         this.version = builder.version;
         this.streamReadConstraints = 
builder.streamReadConstraintsBuilder.build();
         this.typeInfo = builder.typeInfo;
+        this.traversalStrategyResolver = TraversalStrategyResolver.build().
+                
addAllowedTraversalStrategies(builder.allowedTraversalStrategies).create();
     }
 
     @Override
@@ -86,7 +91,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> {
         final ObjectMapper om = new 
ObjectMapper(JsonFactory.builder().streamReadConstraints(streamReadConstraints).build());
         om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
 
-        final GraphSONModule graphSONModule = 
version.getBuilder().create(normalize, typeInfo);
+        final GraphSONModule graphSONModule = 
version.getBuilder().create(normalize, typeInfo, traversalStrategyResolver);
         om.registerModule(graphSONModule);
         customModules.forEach(om::registerModule);
 
@@ -174,6 +179,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> 
{
         builder.normalize = mapper.normalize;
         builder.typeInfo = mapper.typeInfo;
         builder.streamReadConstraintsBuilder = 
mapper.streamReadConstraints.rebuild();
+        builder.allowedTraversalStrategies = new 
ArrayList<>(mapper.traversalStrategyResolver.getAllowedStrategies());
 
         return builder;
     }
@@ -196,6 +202,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> 
{
     public static class Builder implements Mapper.Builder<Builder> {
         private List<SimpleModule> customModules = new ArrayList<>();
         private List<GraphSONModule.GraphSONModuleBuilder> 
customModuleBuilders = new ArrayList<>();
+        private List<Class<? extends TraversalStrategy>> 
allowedTraversalStrategies = new ArrayList<>();
         private boolean loadCustomModules = false;
         private boolean normalize = false;
         private List<IoRegistry> registries = new ArrayList<>();
@@ -252,6 +259,14 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
             return this;
         }
 
+        /**
+         * Allow a provider supplied {@link TraversalStrategy} to be 
deserialized from GraphSON.
+         */
+        public Builder addAllowedTraversalStrategy(final Class<? extends 
TraversalStrategy> strategyClass) {
+            this.allowedTraversalStrategies.add(strategyClass);
+            return this;
+        }
+
         /**
          * Supply a default extension module of V2_0 and V3_0 for 
serialization/deserialization.
          */
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index b3d0443131..5a2ed4baeb 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -43,6 +43,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy;
@@ -137,7 +138,10 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
      */
     private static Optional<Class<?>> tryLoadSparqlStrategy() {
         try {
-            return 
Optional.of(Class.forName("org.apache.tinkerpop.gremlin.sparql.process.traversal.strategy.SparqlStrategy"));
+            return Optional.of(Class.forName(
+                    
"org.apache.tinkerpop.gremlin.sparql.process.traversal.strategy.SparqlStrategy",
+                    false,
+                    GraphSONModule.class.getClassLoader()));
         } catch (Exception ignored) {
             return Optional.empty();
         }
@@ -244,6 +248,11 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
          * Constructs a new object.
          */
         protected GraphSONModuleV3(final boolean normalize, final TypeInfo 
typeInfo) {
+            this(normalize, typeInfo, 
TraversalStrategyResolver.defaultResolver());
+        }
+
+        protected GraphSONModuleV3(final boolean normalize, final TypeInfo 
typeInfo,
+                                   final TraversalStrategyResolver 
traversalStrategyResolver) {
             super("graphson-3.0");
 
             /////////////////////// SERIALIZERS ////////////////////////////
@@ -307,7 +316,7 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
             addDeserializer(Metrics.class, new 
GraphSONSerializersV3.MetricsJacksonDeserializer());
             addDeserializer(TraversalMetrics.class, new 
GraphSONSerializersV3.TraversalMetricsJacksonDeserializer());
             addDeserializer(Tree.class, new 
GraphSONSerializersV3.TreeJacksonDeserializer());
-            addDeserializer(TraversalStrategyProxy.class, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer<>());
+            addDeserializer(TraversalStrategyProxy.class, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer<>(traversalStrategyResolver));
 
             // java.util - use the standard jackson serializers for 
collections when types aren't embedded
             if (typeInfo != TypeInfo.NO_TYPES) {
@@ -380,9 +389,9 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
                     //
                     GraphFilterStrategy.class,
                     VertexProgramStrategy.class
-            ).forEach(strategy -> addDeserializer(strategy, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer(strategy)));
+            ).forEach(strategy -> addDeserializer(strategy, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer(strategy, 
traversalStrategyResolver)));
 
-            GraphSONModule.tryLoadSparqlStrategy().ifPresent(s -> 
addDeserializer(s, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer(s)));
+            GraphSONModule.tryLoadSparqlStrategy().ifPresent(s -> 
addDeserializer(s, new 
TraversalSerializersV3.TraversalStrategyProxyJacksonDeserializer(s, 
traversalStrategyResolver)));
         }
 
         public static Builder build() {
@@ -409,6 +418,12 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
                 return new GraphSONModuleV3(normalize, typeInfo);
             }
 
+            @Override
+            public GraphSONModule create(final boolean normalize, final 
TypeInfo typeInfo,
+                                         final TraversalStrategyResolver 
traversalStrategyResolver) {
+                return new GraphSONModuleV3(normalize, typeInfo, 
traversalStrategyResolver);
+            }
+
         }
     }
 
@@ -508,6 +523,10 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
          * Constructs a new object.
          */
         protected GraphSONModuleV2(final boolean normalize) {
+            this(normalize, TraversalStrategyResolver.defaultResolver());
+        }
+
+        protected GraphSONModuleV2(final boolean normalize, final 
TraversalStrategyResolver traversalStrategyResolver) {
             super("graphson-2.0");
 
             /////////////////////// SERIALIZERS ////////////////////////////
@@ -565,7 +584,7 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
             addDeserializer(Metrics.class, new 
GraphSONSerializersV2.MetricsJacksonDeserializer());
             addDeserializer(TraversalMetrics.class, new 
GraphSONSerializersV2.TraversalMetricsJacksonDeserializer());
             addDeserializer(Tree.class, new 
GraphSONSerializersV2.TreeJacksonDeserializer());
-            addDeserializer(TraversalStrategyProxy.class, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer<>());
+            addDeserializer(TraversalStrategyProxy.class, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer<>(traversalStrategyResolver));
 
             // numbers
             addDeserializer(Integer.class, new 
GraphSONSerializersV2.IntegerJacksonDeserializer());
@@ -630,9 +649,9 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
                     //
                     GraphFilterStrategy.class,
                     VertexProgramStrategy.class
-            ).forEach(strategy -> addDeserializer(strategy, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer(strategy)));
+            ).forEach(strategy -> addDeserializer(strategy, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer(strategy, 
traversalStrategyResolver)));
 
-            GraphSONModule.tryLoadSparqlStrategy().ifPresent(s -> 
addDeserializer(s, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer(s)));
+            GraphSONModule.tryLoadSparqlStrategy().ifPresent(s -> 
addDeserializer(s, new 
TraversalSerializersV2.TraversalStrategyProxyJacksonDeserializer(s, 
traversalStrategyResolver)));
         }
 
         public static Builder build() {
@@ -659,6 +678,12 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
                 return new GraphSONModuleV2(normalize);
             }
 
+            @Override
+            public GraphSONModule create(final boolean normalize, final 
TypeInfo typeInfo,
+                                         final TraversalStrategyResolver 
traversalStrategyResolver) {
+                return new GraphSONModuleV2(normalize, 
traversalStrategyResolver);
+            }
+
         }
     }
 
@@ -758,5 +783,10 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
          * @param typeInfo allows the module to react to the specified 
typeinfo given to the mapper
          */
         GraphSONModule create(final boolean normalize, final TypeInfo 
typeInfo);
+
+        default GraphSONModule create(final boolean normalize, final TypeInfo 
typeInfo,
+                                      final TraversalStrategyResolver 
traversalStrategyResolver) {
+            return create(normalize, typeInfo);
+        }
     }
 }
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..8b88514ff3 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
@@ -31,6 +31,7 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 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;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -523,15 +524,27 @@ final class TraversalSerializersV2 {
     final static class TraversalStrategyProxyJacksonDeserializer<T extends 
TraversalStrategy> extends AbstractObjectDeserializer<TraversalStrategyProxy> {
 
         private Class<T> clazz;
+        private final TraversalStrategyResolver traversalStrategyResolver;
 
         public TraversalStrategyProxyJacksonDeserializer() {
+            this(TraversalStrategyResolver.defaultResolver());
+        }
+
+        public TraversalStrategyProxyJacksonDeserializer(final 
TraversalStrategyResolver traversalStrategyResolver) {
             super(TraversalStrategyProxy.class);
             this.clazz = null;
+            this.traversalStrategyResolver = traversalStrategyResolver;
         }
 
         public TraversalStrategyProxyJacksonDeserializer(final Class<T> clazz) 
{
+            this(clazz, TraversalStrategyResolver.defaultResolver());
+        }
+
+        public TraversalStrategyProxyJacksonDeserializer(final Class<T> clazz,
+                                                        final 
TraversalStrategyResolver traversalStrategyResolver) {
             super(TraversalStrategyProxy.class);
             this.clazz = clazz;
+            this.traversalStrategyResolver = traversalStrategyResolver;
         }
 
         @Override
@@ -544,11 +557,10 @@ 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 Object fqcn = data.get("fqcn");
+                if (null == fqcn)
+                    throw new IllegalArgumentException("TraversalStrategy fqcn 
is required");
+                clasz = traversalStrategyResolver.resolve(fqcn.toString());
             } 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..77cc153b1f 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
@@ -32,6 +32,7 @@ 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;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyResolver;
 import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
@@ -616,15 +617,27 @@ final class TraversalSerializersV3 {
     final static class TraversalStrategyProxyJacksonDeserializer<T extends 
TraversalStrategy> extends AbstractObjectDeserializer<TraversalStrategyProxy> {
 
         private Class<T> clazz;
+        private final TraversalStrategyResolver traversalStrategyResolver;
 
         public TraversalStrategyProxyJacksonDeserializer() {
+            this(TraversalStrategyResolver.defaultResolver());
+        }
+
+        public TraversalStrategyProxyJacksonDeserializer(final 
TraversalStrategyResolver traversalStrategyResolver) {
             super(TraversalStrategyProxy.class);
             this.clazz = null;
+            this.traversalStrategyResolver = traversalStrategyResolver;
         }
 
         public TraversalStrategyProxyJacksonDeserializer(final Class<T> clazz) 
{
+            this(clazz, TraversalStrategyResolver.defaultResolver());
+        }
+
+        public TraversalStrategyProxyJacksonDeserializer(final Class<T> clazz,
+                                                        final 
TraversalStrategyResolver traversalStrategyResolver) {
             super(TraversalStrategyProxy.class);
             this.clazz = clazz;
+            this.traversalStrategyResolver = traversalStrategyResolver;
         }
 
         @Override
@@ -637,11 +650,10 @@ 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 Object fqcn = data.get("fqcn");
+                if (null == fqcn)
+                    throw new IllegalArgumentException("TraversalStrategy fqcn 
is required");
+                clasz = traversalStrategyResolver.resolve(fqcn.toString());
             } else {
                 clasz = this.clazz;
             }
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslatorTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslatorTest.java
index 13a9232158..472e258dcf 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslatorTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslatorTest.java
@@ -22,11 +22,18 @@ package org.apache.tinkerpop.gremlin.jsr223;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TestTraversalStrategies.UnregisteredTraversalStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.junit.Test;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 public class JavaTranslatorTest {
     private GraphTraversalSource g = EmptyGraph.instance().traversal();
@@ -86,4 +93,28 @@ public class JavaTranslatorTest {
         assertEquals(g.E().has("weight", (String) null).asAdmin(), 
translation);
     }
 
-}
\ No newline at end of file
+    @Test
+    public void shouldTranslateRegisteredTraversalStrategyProxy() {
+        final Bytecode bytecode = new Bytecode();
+        bytecode.addSource(TraversalSource.Symbols.withStrategies, new 
TraversalStrategyProxy(ReadOnlyStrategy.instance()));
+        bytecode.addStep("V");
+        final Traversal.Admin<?, ?> translation = 
translator.translate(bytecode);
+        
assertEquals(g.withStrategies(ReadOnlyStrategy.instance()).V().asAdmin(), 
translation);
+    }
+
+    @Test
+    public void shouldRejectUnregisteredTraversalStrategyProxy() {
+        final Bytecode bytecode = new Bytecode();
+        bytecode.addSource(TraversalSource.Symbols.withStrategies, new 
TraversalStrategyProxy(UnregisteredTraversalStrategy.instance()));
+        bytecode.addStep("V");
+
+        try {
+            translator.translate(bytecode);
+            fail("Should have rejected an unregistered traversal strategy 
proxy");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(IllegalStateException.class));
+            assertEquals("TraversalStrategy class is not allowed: " + 
UnregisteredTraversalStrategy.class.getName(), ex.getMessage());
+        }
+    }
+
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TestTraversalStrategies.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TestTraversalStrategies.java
new file mode 100644
index 0000000000..46e245ea89
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TestTraversalStrategies.java
@@ -0,0 +1,79 @@
+/*
+ * 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.process.traversal.strategy;
+
+import org.apache.commons.configuration2.BaseConfiguration;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.util.GremlinDisabledListDelimiterHandler;
+
+public final class TestTraversalStrategies {
+
+    private TestTraversalStrategies() {
+    }
+
+    public static class DummyTraversalStrategy extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+            implements TraversalStrategy.DecorationStrategy {
+        private static final DummyTraversalStrategy INSTANCE = new 
DummyTraversalStrategy();
+
+        protected DummyTraversalStrategy() {
+        }
+
+        public static DummyTraversalStrategy instance() {
+            return INSTANCE;
+        }
+
+        @Override
+        public void apply(final Traversal.Admin<?, ?> traversal) {
+            // do nothing
+        }
+    }
+
+    public static final class DummyConfiguredTraversalStrategy extends 
DummyTraversalStrategy {
+        private static final DummyConfiguredTraversalStrategy INSTANCE = new 
DummyConfiguredTraversalStrategy();
+
+        private DummyConfiguredTraversalStrategy() {
+        }
+
+        public static DummyConfiguredTraversalStrategy instance() {
+            return INSTANCE;
+        }
+
+        @Override
+        public Configuration getConfiguration() {
+            final BaseConfiguration conf = new BaseConfiguration();
+            
conf.setListDelimiterHandler(GremlinDisabledListDelimiterHandler.instance());
+            conf.setProperty("x", 123);
+            conf.setProperty("y", "test");
+            return conf;
+        }
+    }
+
+    public static final class UnregisteredTraversalStrategy extends 
DummyTraversalStrategy {
+        private static final UnregisteredTraversalStrategy INSTANCE = new 
UnregisteredTraversalStrategy();
+
+        private UnregisteredTraversalStrategy() {
+        }
+
+        public static UnregisteredTraversalStrategy instance() {
+            return INSTANCE;
+        }
+    }
+}
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..1a1bcf6a5c 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
@@ -18,27 +18,25 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.graphson;
 
-import org.apache.commons.configuration2.BaseConfiguration;
-import org.apache.commons.configuration2.Configuration;
 import 
org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 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.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TestTraversalStrategies.DummyConfiguredTraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TestTraversalStrategies.DummyTraversalStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy;
 import 
org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
-import org.apache.tinkerpop.gremlin.util.GremlinDisabledListDelimiterHandler;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+import 
org.apache.tinkerpop.shaded.jackson.databind.exc.MismatchedInputException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -73,8 +71,10 @@ import static org.hamcrest.Matchers.either;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.core.AnyOf.anyOf;
 import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeThat;
 
 /**
@@ -89,9 +89,13 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
                 {"v1", 
GraphSONMapper.build().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()},
                 {"v2", GraphSONMapper.build().version(GraphSONVersion.V2_0)
                         .addCustomModule(GraphSONXModuleV2.build())
+                        
.addAllowedTraversalStrategy(DummyTraversalStrategy.class)
+                        
.addAllowedTraversalStrategy(DummyConfiguredTraversalStrategy.class)
                         
.typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()},
                 {"v3", GraphSONMapper.build().version(GraphSONVersion.V3_0)
                         .addCustomModule(GraphSONXModuleV3.build())
+                        
.addAllowedTraversalStrategy(DummyTraversalStrategy.class)
+                        
.addAllowedTraversalStrategy(DummyConfiguredTraversalStrategy.class)
                         
.typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()}
         });
     }
@@ -533,48 +537,24 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
     }
 
     @Test
-    public void shouldHandlePExt() throws Exception  {
-        assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
-
-        final P o = PExt.mix("bah");
-        assertEquals(o, serializeDeserialize(mapper, o, P.class));
-    }
-
-    public static class DummyTraversalStrategy implements TraversalStrategy {
-
-        private static final DummyTraversalStrategy INSTANCE = new 
DummyTraversalStrategy();
-
-        @Override
-        public void apply(final Traversal.Admin traversal) {
-            // do nothing
-        }
-
-        @Override
-        public int compareTo(final Object o) {
-            return 0;
-        }
+    public void shouldRejectUnregisteredTraversalStrategyProxy() throws 
Exception {
+        assumeThat(version,  either(startsWith("v2")).or(startsWith("v3")));
 
-        public static DummyTraversalStrategy instance() {
-            return INSTANCE;
+        try {
+            
mapper.readValue("{\"@type\":\"g:TraversalStrategy\",\"@value\":{\"conf\":{},\"fqcn\":\"java.lang.Runtime\"}}",
 TraversalStrategyProxy.class);
+            fail("Should have rejected an unregistered traversal strategy 
proxy");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(MismatchedInputException.class));
+            assertThat(ex.getMessage(), containsString("TraversalStrategy 
class is not allowed: java.lang.Runtime"));
         }
     }
 
-    public static class DummyConfiguredTraversalStrategy extends 
DummyTraversalStrategy {
-
-        private static final DummyConfiguredTraversalStrategy INSTANCE = new 
DummyConfiguredTraversalStrategy();
-
-        @Override
-        public Configuration getConfiguration() {
-            final BaseConfiguration conf = new BaseConfiguration();
-            
conf.setListDelimiterHandler(GremlinDisabledListDelimiterHandler.instance());
-            conf.setProperty("x", 123);
-            conf.setProperty("y", "test");
-            return conf;
-        }
+    @Test
+    public void shouldHandlePExt() throws Exception  {
+        assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
 
-        public static DummyConfiguredTraversalStrategy instance() {
-            return INSTANCE;
-        }
+        final P o = PExt.mix("bah");
+        assertEquals(o, serializeDeserialize(mapper, o, P.class));
     }
 
     public static class PExt<V> extends P<V> {
diff --git 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV2.java
 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV2.java
index bf66da59e3..bdbc8f6aae 100644
--- 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV2.java
+++ 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV2.java
@@ -74,6 +74,7 @@ public abstract class AbstractGraphSONMessageSerializerV2 
extends AbstractMessag
     public void configure(final Map<String, Object> config, final Map<String, 
Graph> graphs) {
         final GraphSONMapper.Builder initialBuilder = initBuilder(null);
         addIoRegistries(config, initialBuilder);
+        addAllowedTraversalStrategies(config, initialBuilder);
         applyMaxTokenLimits(initialBuilder, config);
         mapper = configureBuilder(initialBuilder).create().createMapper();
     }
diff --git 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
index 03f570aa5c..9fb368a3a4 100644
--- 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
+++ 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
@@ -18,11 +18,15 @@
  */
 package org.apache.tinkerpop.gremlin.util.ser;
 
-import org.apache.tinkerpop.gremlin.util.MessageSerializer;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.Mapper;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
+import org.apache.tinkerpop.gremlin.util.MessageSerializer;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +39,7 @@ import java.util.Map;
  */
 public abstract class AbstractMessageSerializer<M> implements 
MessageSerializer<M> {
     public static final String TOKEN_IO_REGISTRIES = "ioRegistries";
+    public static final String TOKEN_ALLOWED_TRAVERSAL_STRATEGIES = 
"allowedTraversalStrategies";
 
     /**
      * Reads a list of fully qualified class names from the value of the 
{@link #TOKEN_IO_REGISTRIES} configuration
@@ -84,6 +89,38 @@ public abstract class AbstractMessageSerializer<M> 
implements MessageSerializer<
         return instanceMethod;
     }
 
+    /**
+     * Reads a list of fully qualified {@link TraversalStrategy} class names 
that are allowed to be deserialized from
+     * typed request or response data.
+     */
+    protected void addAllowedTraversalStrategies(final Map<String, Object> 
config, final GraphSONMapper.Builder builder) {
+        
getTraversalStrategyClassesFromConfig(config).forEach(builder::addAllowedTraversalStrategy);
+    }
+
+    /**
+     * Reads a list of fully qualified {@link TraversalStrategy} class names 
that are allowed to be deserialized from
+     * typed request or response data.
+     */
+    protected void addAllowedTraversalStrategies(final Map<String, Object> 
config, final TypeSerializerRegistry.Builder builder) {
+        
getTraversalStrategyClassesFromConfig(config).forEach(builder::addAllowedTraversalStrategy);
+    }
+
+    private List<Class<? extends TraversalStrategy>> 
getTraversalStrategyClassesFromConfig(final Map<String, Object> config) {
+        final List<Class<? extends TraversalStrategy>> 
traversalStrategyClasses = new ArrayList<>();
+        for (String className : 
getListStringFromConfig(TOKEN_ALLOWED_TRAVERSAL_STRATEGIES, config)) {
+            try {
+                final Class<?> strategyClass = Class.forName(className, false, 
this.getClass().getClassLoader());
+                if (!TraversalStrategy.class.isAssignableFrom(strategyClass))
+                    throw new IllegalStateException(String.format("%s is not a 
TraversalStrategy", className));
+                traversalStrategyClasses.add((Class<? extends 
TraversalStrategy>) strategyClass);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        return traversalStrategyClasses;
+    }
+
     /**
      * Gets a {@link List} of strings from the configuration object.
      */
@@ -98,4 +135,4 @@ public abstract class AbstractMessageSerializer<M> 
implements MessageSerializer<
 
         return classNameList;
     }
-}
\ No newline at end of file
+}
diff --git 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV1.java
 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV1.java
index 5d7977b2ba..52a6d42d89 100644
--- 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV1.java
+++ 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV1.java
@@ -126,6 +126,7 @@ public class GraphBinaryMessageSerializerV1 extends 
AbstractMessageSerializer<Gr
             }
         });
 
+        addAllowedTraversalStrategies(config, builder);
         addCustomClasses(config, builder);
 
         this.serializeToString = 
Boolean.parseBoolean(config.getOrDefault(TOKEN_SERIALIZE_RESULT_TO_STRING, 
"false").toString());
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/TestTraversalStrategies.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/TestTraversalStrategies.java
new file mode 100644
index 0000000000..c78ca36dcb
--- /dev/null
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/TestTraversalStrategies.java
@@ -0,0 +1,46 @@
+/*
+ * 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.util;
+
+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;
+
+public final class TestTraversalStrategies {
+
+    private TestTraversalStrategies() {
+    }
+
+    public static final class DummyTraversalStrategy extends 
AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy>
+            implements TraversalStrategy.DecorationStrategy {
+        private static final DummyTraversalStrategy INSTANCE = new 
DummyTraversalStrategy();
+
+        private DummyTraversalStrategy() {
+        }
+
+        public static DummyTraversalStrategy instance() {
+            return INSTANCE;
+        }
+
+        @Override
+        public void apply(final Traversal.Admin<?, ?> traversal) {
+            // do nothing
+        }
+    }
+}
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..0946a47f41 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,15 +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.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
-import org.apache.tinkerpop.gremlin.util.MessageSerializer;
-import org.apache.tinkerpop.gremlin.util.Tokens;
-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.TraversalStrategyProxy;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Property;
@@ -37,14 +33,22 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONXModuleV3;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.apache.tinkerpop.gremlin.util.MessageSerializer;
+import 
org.apache.tinkerpop.gremlin.util.TestTraversalStrategies.DummyTraversalStrategy;
+import org.apache.tinkerpop.gremlin.util.Tokens;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+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.shaded.jackson.databind.JsonMappingException;
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -403,6 +407,26 @@ public class GraphSONMessageSerializerV3Test {
         }
     }
 
+    @Test
+    public void shouldDeserializeConfiguredTraversalStrategy() throws 
SerializationException {
+        final GraphSONMessageSerializerV3 serializer = new 
GraphSONMessageSerializerV3();
+        final Map<String, Object> config = new HashMap<>();
+        
config.put(AbstractMessageSerializer.TOKEN_ALLOWED_TRAVERSAL_STRATEGIES,
+                
Collections.singletonList(DummyTraversalStrategy.class.getName()));
+        serializer.configure(config, Collections.emptyMap());
+
+        final String request = 
String.format("{\"requestId\":{\"@type\":\"g:UUID\",\"@value\":\"0397b9c0-ffab-470e-a6a8-644fc80c01d6\"},\"op\":\"bytecode\",\"processor\":\"traversal\",\"args\":{\"gremlin\":{\"@type\":\"g:Bytecode\",\"@value\":{\"source\":[[\"withStrategies\",{\"@type\":\"g:TraversalStrategy\",\"@value\":{\"fqcn\":\"%s\",\"conf\":{}}}]],\"step\":[[\"V\"]]}},\"aliases\":{\"g\":\"g\"}}}",
+                DummyTraversalStrategy.class.getName());
+        final ByteBuf buffer = allocator.buffer(request.length());
+        buffer.writeBytes(request.getBytes());
+
+        final RequestMessage deserialized = 
serializer.deserializeRequest(buffer);
+        final Bytecode deserializedBytecode = (Bytecode) 
deserialized.getArgs().get(Tokens.ARGS_GREMLIN);
+        final TraversalStrategyProxy strategy = (TraversalStrategyProxy) 
deserializedBytecode.getSourceInstructions().get(0).getArguments()[0];
+
+        assertEquals(DummyTraversalStrategy.class, 
strategy.getStrategyClass());
+    }
+
     private void assertCommon(final ResponseMessage response) {
         assertEquals(requestId, response.getRequestId());
         assertEquals(ResponseStatusCode.SUCCESS, 
response.getStatus().getCode());
@@ -416,4 +440,5 @@ public class GraphSONMessageSerializerV3Test {
     private ResponseMessage convert(final Object toSerialize) throws 
SerializationException {
         return convert(toSerialize, this.serializer);
     }
+
 }
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryMessageSerializerV1Test.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryMessageSerializerV1Test.java
index b1e42d56ae..7986134a33 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryMessageSerializerV1Test.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryMessageSerializerV1Test.java
@@ -20,16 +20,20 @@ package org.apache.tinkerpop.gremlin.util.ser.binary;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import 
org.apache.tinkerpop.gremlin.util.TestTraversalStrategies.DummyTraversalStrategy;
 import org.apache.tinkerpop.gremlin.util.Tokens;
 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.util.ser.AbstractMessageSerializer;
 import org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1;
 import org.apache.tinkerpop.gremlin.util.ser.SerializationException;
-import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
-import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.junit.Test;
 
 import java.nio.charset.StandardCharsets;
@@ -40,9 +44,11 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static 
org.apache.tinkerpop.gremlin.util.MockitoHamcrestMatcherAdapter.reflectionEquals;
-import static org.junit.Assert.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasItemInArray;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 public class GraphBinaryMessageSerializerV1Test {
     private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
@@ -194,13 +200,47 @@ public class GraphBinaryMessageSerializerV1Test {
         assertEquals(1, counter);
     }
 
-    @Test(expected = IllegalStateException.class)
+    @Test
     public void shouldThrowWhenConfigurationOfRegistryBuilderFails() {
         final GraphBinaryMessageSerializerV1 serializer = new 
GraphBinaryMessageSerializerV1();
         final Map<String, Object> config = new HashMap<>();
         config.put(GraphBinaryMessageSerializerV1.TOKEN_BUILDER, 
"org.apache.tinkerpop.gremlin.util.ser.binary.NonExistentClass");
 
-        serializer.configure(config, null);
+        try {
+            serializer.configure(config, null);
+            fail("Should have failed to configure a missing registry builder");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(IllegalStateException.class));
+            assertEquals("java.lang.ClassNotFoundException: 
org.apache.tinkerpop.gremlin.util.ser.binary.NonExistentClass", 
ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldDeserializeConfiguredTraversalStrategy() throws 
SerializationException {
+        final GraphBinaryMessageSerializerV1 serializer = new 
GraphBinaryMessageSerializerV1();
+        final Map<String, Object> config = new HashMap<>();
+        
config.put(AbstractMessageSerializer.TOKEN_ALLOWED_TRAVERSAL_STRATEGIES,
+                
Collections.singletonList(DummyTraversalStrategy.class.getName()));
+        serializer.configure(config, Collections.emptyMap());
+
+        final Bytecode bytecode = new Bytecode();
+        bytecode.addSource("withStrategies", 
DummyTraversalStrategy.instance());
+        bytecode.addStep("V");
+
+        final RequestMessage request = 
RequestMessage.build(Tokens.OPS_BYTECODE)
+                .processor("traversal")
+                .overrideRequestId(UUID.randomUUID())
+                .addArg(Tokens.ARGS_GREMLIN, bytecode)
+                .create();
+        final ByteBuf buffer = serializer.serializeRequestAsBinary(request, 
allocator);
+        final int mimeLength = buffer.readByte();
+        buffer.skipBytes(mimeLength);
+
+        final RequestMessage deserialized = 
serializer.deserializeRequest(buffer);
+        final Bytecode deserializedBytecode = (Bytecode) 
deserialized.getArgs().get(Tokens.ARGS_GREMLIN);
+        final TraversalStrategyProxy strategy = (TraversalStrategyProxy) 
deserializedBytecode.getSourceInstructions().get(0).getArguments()[0];
+
+        assertEquals(DummyTraversalStrategy.class, 
strategy.getStrategyClass());
     }
 
     @Test
@@ -276,4 +316,5 @@ public class GraphBinaryMessageSerializerV1Test {
             return super.create();
         }
     }
+
 }
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java
new file mode 100644
index 0000000000..8c284bbd0f
--- /dev/null
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/TraversalStrategySerializerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.util.ser.binary.types;
+
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.structure.io.Buffer;
+import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
+import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
+import 
org.apache.tinkerpop.gremlin.structure.io.binary.types.TraversalStrategySerializer;
+import 
org.apache.tinkerpop.gremlin.util.TestTraversalStrategies.DummyTraversalStrategy;
+import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class TraversalStrategySerializerTest {
+    private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
+    private final NettyBufferFactory bufferFactory = new NettyBufferFactory();
+
+    @Test
+    public void shouldRejectUnregisteredTraversalStrategy() throws Exception {
+        final GraphBinaryWriter writer = new GraphBinaryWriter();
+        final GraphBinaryReader reader = new GraphBinaryReader();
+        final Buffer buffer = bufferFactory.create(allocator.buffer());
+        buffer.writeByte(DataType.TRAVERSALSTRATEGY.getCodeByte());
+        buffer.writeByte(0);
+        writer.writeValue("java.lang.Runtime", buffer, false);
+        writer.writeValue(Collections.emptyMap(), buffer, false);
+        buffer.readerIndex(0);
+
+        try {
+            reader.read(buffer);
+            fail("Should have rejected an unregistered traversal strategy");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(IllegalArgumentException.class));
+            assertEquals("TraversalStrategy class is not allowed: 
java.lang.Runtime", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldReadWriteRegisteredTraversalStrategy() throws Exception {
+        final TypeSerializerRegistry registry = TypeSerializerRegistry.build().
+                
addAllowedTraversalStrategy(DummyTraversalStrategy.class).create();
+        assertReadWriteRegisteredTraversalStrategy(registry);
+    }
+
+    @Test
+    public void shouldMergeAllowedTraversalStrategyWithRegisteredSerializer() 
throws Exception {
+        final TypeSerializerRegistry registry = TypeSerializerRegistry.build().
+                add(TraversalStrategy.class, new 
TraversalStrategySerializer()).
+                
addAllowedTraversalStrategy(DummyTraversalStrategy.class).create();
+        assertReadWriteRegisteredTraversalStrategy(registry);
+    }
+
+    private void assertReadWriteRegisteredTraversalStrategy(final 
TypeSerializerRegistry registry) throws Exception {
+        final GraphBinaryWriter writer = new GraphBinaryWriter(registry);
+        final GraphBinaryReader reader = new GraphBinaryReader(registry);
+        final Buffer buffer = bufferFactory.create(allocator.buffer());
+
+        writer.write(DummyTraversalStrategy.instance(), buffer);
+        buffer.readerIndex(0);
+        final TraversalStrategyProxy strategy = reader.read(buffer);
+
+        assertEquals(DummyTraversalStrategy.class, 
strategy.getStrategyClass());
+    }
+}

Reply via email to