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

imbajin pushed a commit to branch cx/bump-server-api-version
in repository https://gitbox.apache.org/repos/asf/hugegraph.git

commit 0752ec20af2230b884b50a7570b9eec96279654d
Author: dark <[email protected]>
AuthorDate: Mon Aug 17 23:39:13 2026 +0800

    fix(server): enforce Gremlin mutations
    
    - classify mutation steps from Gremlin bytecode
    - require write access for add and property steps
    - require delete access for drop steps
    - cover read write delete and nested traversals
---
 .../apache/hugegraph/auth/HugeGraphAuthProxy.java  | 34 ++++++++++++++++++
 .../unit/auth/HugeGraphAuthProxyTest.java          | 40 ++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
index 4b0aed578..5b4e70460 100644
--- 
a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
+++ 
b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/HugeGraphAuthProxy.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -95,6 +96,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Script;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal.Symbols;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import 
org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
 import org.apache.tinkerpop.gremlin.structure.Edge;
@@ -2414,6 +2416,11 @@ public final class HugeGraphAuthProxy implements 
HugeGraph {
              */
             String caller = Thread.currentThread().getName();
             if (!caller.contains(TraversalStrategiesProxy.REST_WORKER)) {
+                for (HugePermission permission :
+                     traversalPermissions(traversal.getBytecode())) {
+                    verifyNamePermission(permission, ResourceType.GREMLIN,
+                                         script);
+                }
                 verifyNamePermission(HugePermission.EXECUTE,
                                      ResourceType.GREMLIN, script);
             }
@@ -2461,4 +2468,31 @@ public final class HugeGraphAuthProxy implements 
HugeGraph {
             return this.origin.toString();
         }
     }
+
+    private static Set<HugePermission> traversalPermissions(Bytecode bytecode) 
{
+        Set<HugePermission> permissions = EnumSet.noneOf(HugePermission.class);
+        collectTraversalPermissions(bytecode, permissions);
+        return permissions;
+    }
+
+    private static void collectTraversalPermissions(
+                        Bytecode bytecode,
+                        Set<HugePermission> permissions) {
+        for (Instruction instruction : bytecode.getStepInstructions()) {
+            String operator = instruction.getOperator();
+            if (Symbols.addV.equals(operator) ||
+                Symbols.addE.equals(operator) ||
+                Symbols.property.equals(operator)) {
+                permissions.add(HugePermission.WRITE);
+            } else if (Symbols.drop.equals(operator)) {
+                permissions.add(HugePermission.DELETE);
+            }
+            for (Object argument : instruction.getArguments()) {
+                if (argument instanceof Bytecode) {
+                    collectTraversalPermissions((Bytecode) argument,
+                                                permissions);
+                }
+            }
+        }
+    }
 }
diff --git 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/auth/HugeGraphAuthProxyTest.java
 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/auth/HugeGraphAuthProxyTest.java
index 1b209c913..7b1ae32e2 100644
--- 
a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/auth/HugeGraphAuthProxyTest.java
+++ 
b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/auth/HugeGraphAuthProxyTest.java
@@ -19,13 +19,16 @@ package org.apache.hugegraph.unit.auth;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.hugegraph.HugeGraph;
 import org.apache.hugegraph.auth.AuthManager;
 import org.apache.hugegraph.auth.HugeAuthenticator;
 import org.apache.hugegraph.auth.HugeDefaultRole;
 import org.apache.hugegraph.auth.HugeGraphAuthProxy;
+import org.apache.hugegraph.auth.HugePermission;
 import org.apache.hugegraph.auth.RolePermission;
 import org.apache.hugegraph.auth.UserWithRole;
 import org.apache.hugegraph.backend.id.IdGenerator;
@@ -44,6 +47,8 @@ import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.appender.AbstractAppender;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.config.Property;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal.Symbols;
 import org.junit.After;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -366,6 +371,41 @@ public class HugeGraphAuthProxyTest extends BaseUnitTest {
         }
     }
 
+    @Test
+    public void testTraversalPermissions() throws Exception {
+        Bytecode read = new Bytecode();
+        read.addStep(Symbols.V);
+        Assert.assertTrue(traversalPermissions(read).isEmpty());
+
+        Bytecode write = new Bytecode();
+        write.addStep(Symbols.addV, "person");
+        write.addStep(Symbols.property, "name", "marko");
+        Assert.assertEquals(Collections.singleton(HugePermission.WRITE),
+                            traversalPermissions(write));
+
+        Bytecode delete = new Bytecode();
+        delete.addStep(Symbols.V);
+        delete.addStep(Symbols.drop);
+        Assert.assertEquals(Collections.singleton(HugePermission.DELETE),
+                            traversalPermissions(delete));
+
+        Bytecode nested = new Bytecode();
+        nested.addStep(Symbols.addE, "knows");
+        Bytecode parent = new Bytecode();
+        parent.addStep(Symbols.sideEffect, nested);
+        Assert.assertEquals(Collections.singleton(HugePermission.WRITE),
+                            traversalPermissions(parent));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Set<HugePermission> traversalPermissions(Bytecode bytecode)
+            throws Exception {
+        Method method = HugeGraphAuthProxy.class.getDeclaredMethod(
+                "traversalPermissions", Bytecode.class);
+        method.setAccessible(true);
+        return (Set<HugePermission>) method.invoke(null, bytecode);
+    }
+
     private static class TestAppender extends AbstractAppender {
 
         private final List<LogEvent> events;

Reply via email to