kenhuuu commented on code in PR #3402:
URL: https://github.com/apache/tinkerpop/pull/3402#discussion_r3205017735


##########
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParser.java:
##########
@@ -91,4 +88,94 @@ public static Object parse(final String query, final 
GremlinVisitor<Object> visi
             throw new GremlinParserException("Failed to interpret Gremlin 
query: " + ex.getMessage(), ex);
         }
     }
+
+    /**
+     * Parses a gremlin-lang map literal string into a {@code Map<String, 
Object>} for use as parameters.
+     * <p>
+     * Uses {@link ParameterMapVisitor} to prevent traversal injection and 
validates that all keys are strings
+     * and no values contain traversals.
+     *
+     * @param parameterMapString the gremlin-lang map literal string (e.g. 
{@code [x:1,y:"marko"]}) or {@code null}/empty
+     * @return the parsed and validated parameter map
+     * @throws GremlinParserException if parsing fails or validation detects 
invalid content
+     */
+    public static Map<String, Object> parseParameters(final String 
parameterMapString) {
+        if (parameterMapString == null || parameterMapString.isEmpty()) {
+            return Map.of();
+        }
+
+        final GremlinParser parser = createParser(parameterMapString);
+        final GremlinParser.GenericMapLiteralContext mapCtx = 
parser.genericMapLiteral();
+
+        final ParameterMapVisitor visitor = new ParameterMapVisitor(new 
GremlinAntlrToJava());
+        final Map<Object, Object> rawMap = (Map<Object, Object>) 
visitor.visitGenericMapLiteral(mapCtx);
+
+        if (rawMap == null) {
+            return Map.of();
+        }
+
+        for (final Map.Entry<?, ?> entry : rawMap.entrySet()) {
+            if (!(entry.getKey() instanceof String)) {
+                throw new GremlinParserException(
+                        String.format("Parameter map keys must be String, 
found: %s",
+                                entry.getKey() == null ? "null" : 
entry.getKey().getClass().getSimpleName()));
+            }
+            final String key = (String) entry.getKey();
+            if (!SourceVersion.isIdentifier(key)) {
+                throw new GremlinParserException(
+                        String.format("Parameter map key must be a valid 
identifier: %s", key));
+            }
+            validateParameterValue(entry.getValue());
+        }
+
+        return (Map<String, Object>) (Map<?, ?>) rawMap;
+    }
+
+    /**
+     * Recursively validates that a parameter value does not contain a {@link 
Traversal}. Nested validation is needed
+     * because steps like mergeV iterate map values, so a Traversal hiding 
inside a nested map or collection would still
+     * be dangerous.
+     */
+    private static void validateParameterValue(final Object value) {
+        if (value instanceof Traversal) {
+            throw new GremlinParserException("Traversals are not allowed as 
parameter values");

Review Comment:
   That's a good point and we might have to relax some of these rules in the 
future.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to