com.arcadedb:arcadedb-gremlin: Gremlin Groovy engine missing scripting authorization check enables authenticated RCE
What fixes it
- 26.10.1com.arcadedb:arcadedb-gremlin, mavenGitHub
<dependency><groupId>com.arcadedb</groupId><artifactId>arcadedb-gremlin</artifactId><version>26.10.1</version></dependency>
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Summary
When the ArcadeDB Gremlin module is configured to use the Groovy script engine (arcadedb.gremlin.engine set to groovy or auto), any authenticated user, including those with only the read-only reader role, can execute arbitrary Groovy code on the server by submitting a Gremlin command to the POST /api/v1/command/{database} HTTP endpoint. The GremlinQueryEngine.command() method performs no scripting authorization check before dispatching to the Groovy engine, and the SecureASTCustomizer applied to the engine is explicitly documented by the developers as ineffective against RCE.
Root Cause
The PolyglotQueryEngine (used for JavaScript/GraalVM languages) gates script execution behind checkScriptingPermissions(), which requires the UPDATE_SECURITY database permission. GremlinQueryEngine has no equivalent authorization check: its command() method dispatches directly to the Groovy engine without verifying the caller's permission level. This gap means the scripting authorization invariant established by CVE-2026-65831 (GHSA-48qw-824m-86pr) was enforced on the JS engine path but never on the Gremlin Groovy path.
// gremlin/src/main/java/com/arcadedb/gremlin/query/GremlinQueryEngine.java:67-82 // command() creates ArcadeGremlin and calls execute() with zero authorization. // Compare engine/src/main/java/com/arcadedb/query/polyglot/PolyglotQueryEngine.java:148-149, // which calls checkScriptingPermissions() before execution.
Impact
An attacker must have valid credentials for any database role (including the lowest-privilege reader role) on an ArcadeDB instance where the Gremlin Groovy engine has been explicitly enabled. The default engine is java, which is not affected. Enabling the Groovy engine requires the UPDATE_DATABASE_SETTINGS permission, which in the default group configuration is admin-only; however, the permission hierarchy deliberately separates UPDATE_DATABASE_SETTINGS from UPDATE_SECURITY, so custom configurations can grant the former without the latter.
Once the Groovy engine is active, any authenticated user can achieve arbitrary OS command execution as the ArcadeDB server process user through a single HTTP request. Demonstrated capabilities include host filesystem read and write, OS command execution, network access, and cross-database privilege escalation via database.getSecurity().createUser().
The SecureASTCustomizer applied to the Groovy engine is not an effective mitigation. The developers document it as "VULNERABLE to RCE attacks" and state "DO NOT RELY ON THIS FOR SECURITY" (see gremlin/src/main/java/com/arcadedb/gremlin/ArcadeGraph.java:706-709). The groovy.lang.Closure type is whitelisted in the receiver list (line 752), which is a known escape vector for SecureASTCustomizer.
Proof of Concept
Requirements: A running ArcadeDB server with the Gremlin module enabled, a database with arcadedb.gremlin.engine set to "groovy", and valid credentials for any role (including reader).
Step 1: An administrator enables the Groovy engine on a target database for compatibility:
ALTER DATABASE `arcadedb.gremlin.engine` `groovy`
Step 2: A read-only user executes an OS command via the Gremlin endpoint:
curl -u reader1:password -X POST \
"http://localhost:2480/api/v1/command/testdb" \
-H "Content-Type: application/json" \
-d '{"language":"gremlin","command":"\"id\".execute().text"}'Expected response: the output of the "id" command showing the UID/GID of the ArcadeDB process.
Attack Chain
- Exposure: The
POST /api/v1/command/{database}HTTP endpoint is the standard command interface, always available when the server module is active. It accepts a user-controlledlanguagefield in the JSON request body. - Control: The attacker fully controls the
language(set togremlin) andcommandfields. The command value flows as the Groovy script body toGremlinGroovyScriptEngine.eval()with no sanitization, transformation, or restriction. - Path:
PostCommandHandler.java:165,424-429extracts the language and passes it todatabase.command().LocalDatabase.java:2235-2244resolves"gremlin"viaQueryEngineManagertoGremlinQueryEngine.GremlinQueryEngine.java:67-82createsArcadeGremlinand callsexecute().ArcadeGremlin.java:345-387reads the engine config and routes toexecuteStatement("groovy", ...)when the Groovy engine is configured. - Guard: No authorization check exists anywhere on this path. An exhaustive search of
gremlin/src/main/java/forcheckScriptingPermissions,checkPermissionsOnDatabase,UPDATE_SECURITY, andSecurityDatabaseUserreturned zero matches in executable code. TheSecureASTCustomizeris explicitly documented as ineffective. - Primitive: Arbitrary Groovy/JVM code execution via
GremlinGroovyScriptEngine. Groovy'sString.execute()method provides direct OS command execution. - Result: The read-only database user achieves OS-level code execution as the ArcadeDB process user, crossing from database authorization scope to host OS scope.
Bypass Evidence
No scripting authorization guard exists on the Gremlin Groovy engine path: the bypass is structural rather than a circumvention of an existing control. The checkScriptingPermissions() method (defined at engine/src/main/java/com/arcadedb/query/polyglot/PolyglotQueryEngine.java:135-137) was added as part of the CVE-2026-65831 fix, but it was implemented only in PolyglotQueryEngine, not in GremlinQueryEngine.
The fix for GHSA-wcm5-4wjm-9wj3 ("silent Groovy fallback") prevents the Java engine from silently falling back to Groovy in strict mode (ArcadeGremlin.java:370-374). It does not add any authorization check to the explicit Groovy configuration path. The GHSA-wcm5 reporter recommended adding checkPermissionsOnDatabase(UPDATE_SECURITY) before Groovy evaluation, but this was not implemented.
The SecureASTCustomizer was evaluated as a potential defense. It is explicitly documented as ineffective by the developers (ArcadeGraph.java:706-709), and groovy.lang.Closure is whitelisted in the receiver list (line 752), providing a known escape vector.
Affected Versions
- Ecosystem: Maven
- Package:
com.arcadedb:arcadedb-gremlin - Confirmed affected range:
<= 26.9.1 - Latest release checked: 26.9.1 on Maven Central (published 2026-09-03)
- Fix status: not fixed in any published or development version
The authorization check has never existed in GremlinQueryEngine in any published version. The vulnerability was introduced when GremlinQueryEngine was first created. HEAD (26.10.1-SNAPSHOT) still lacks the check. The introduction boundary cannot be precisely established from the available history, but the Gremlin module has been published since at least version 21.9.1-beta.
Suggested Fix
Add a checkScriptingPermissions() call (or equivalent checkPermissionsOnDatabase(SecurityDatabaseUser.DATABASE_ACCESS.UPDATE_SECURITY)) to GremlinQueryEngine.command(), matching the existing pattern in PolyglotQueryEngine.command(). The same check should also be applied to GremlinQueryEngine.query() and GremlinQueryEngine.analyze() to match the complete gating in PolyglotQueryEngine.
As an interim mitigation, ensure the Gremlin engine configuration remains at the default value of java. Do not set arcadedb.gremlin.engine to groovy or auto on instances where untrusted users have database access. Restrict the UPDATE_DATABASE_SETTINGS permission to prevent non-administrators from changing the engine configuration.
Reported by zx (GitHub: @manus-pi).
Text from GitHub Advisory Database
Affected versions 1
- com.arcadedb:arcadedb-gremlin, maven<= 26.9.1GitHub