com.arcadedb:arcadedb-bolt: ArcadeDB Bolt protocol: missing per-database authorization lets an authenticated user read another database's schema metadata
What fixes it
- 26.10.1com.arcadedb:arcadedb-bolt, mavenGitHub
<dependency><groupId>com.arcadedb</groupId><artifactId>arcadedb-bolt</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
The ArcadeDB Bolt wire protocol handler (com.arcadedb:arcadedb-bolt) never performs a per-database authorization check when a client selects or switches the active database. A user who holds valid credentials for any single database on the server can send a standard Bolt RUN or BEGIN message whose db field names a database they were not granted, then read that database's full schema metadata (all node and relationship type names, all property keys, and all index and constraint definitions) using the built-in system procedures the handler answers directly. Actual record data stays protected by a deny-all sentinel, but the schema metadata read path bypasses that sentinel entirely. Exploitation needs only network access to the Bolt port (7687, bound to all interfaces by default) and credentials for one database.
Root Cause
Every other ArcadeDB wire protocol gates database selection with canAccessToDatabase(): MongoDB, Redis, PostgreSQL, gRPC, and the HTTP handler family all check the caller's grant before serving a named database. The Bolt handler does not. BoltNetworkExecutor.ensureDatabase() resolves the client-supplied database name and opens it through server.getDatabase(targetName) with no authorization call anywhere on the path. The only security-relevant action it takes is binding a deny-all ServerSecurityDatabaseUser sentinel to the database context, which rejects record-level operations routed through the engine ACL layer (requestAccessOnDatabase, requestAccessOnFile, requestAccessOnType).
However, handleSystemQuery() intercepts system queries (CALL db.labels(), CALL db.relationshipTypes(), CALL db.propertyKeys(), SHOW INDEXES, SHOW CONSTRAINTS) before the engine's ACL-gated execution path is ever reached, and answers them by reading the schema through the direct Java API (database.getSchema().getTypes()). Those direct reads never invoke the ACL layer, so the deny-all sentinel has no effect on them. The security invariant that a user may only observe databases they are authorized for is therefore violated for schema metadata.
Affected functions (bolt/src/main/java/com/arcadedb/bolt/BoltNetworkExecutor.java): ensureDatabase() - opens a user-named database with no canAccessToDatabase() check handleSystemQuery() - serves schema metadata via direct Schema API, bypassing the ACL sentinel Supporting evidence: bolt/src/main/java/com/arcadedb/bolt/BoltSystemProcedures.java (serveSchemaProcedure has zero security imports or permission checks) Contrast (authorization is enforced on the SQL path): engine .../FetchFromSchemaTypesStep.java calls SecurityHelper.canAccessType(user, type, READ_RECORD)
Impact
An attacker who can authenticate to the Bolt endpoint with credentials for any one database (PR:L) can enumerate the complete schema of any other database hosted on the same server instance, regardless of their grants. Disclosed information includes every vertex (node) type name, every edge (relationship) type name, every property key name, and every index and constraint definition (index type, indexed properties, uniqueness, provider). Schema names frequently reveal sensitive data categories and stored fields (for example type names such as CreditCard or property names such as ssn), providing precise reconnaissance for a follow-on attack. This is a confidentiality impact limited to metadata: the deny-all sentinel continues to block reads and writes of actual record data through the engine, so no record contents, no write capability, and no availability impact were demonstrated (C:L/I:N/A:N). The scope is unchanged because the Bolt server is reading its own managed databases under its own security authority (S:U).
Proof of Concept
Requires: a running ArcadeDB server with the Bolt protocol enabled (default), at least two databases, and a non-root user granted access to only one of them. The official Neo4j Python driver drives the Bolt db field via the database= session parameter.
Setup:
- Start an ArcadeDB server (Bolt listens on 7687 by default).
- Create two databases,
public_dbandsecret_db. - Create a user granted access to
public_dbonly.
from neo4j import GraphDatabase
# Credentials the attacker legitimately holds for public_db only.
driver = GraphDatabase.driver("bolt://TARGET_HOST:7687", auth=("limited_user", "PASSWORD"))
# Target a database the user was NOT granted by naming it in the session.
with driver.session(database="secret_db") as s:
print("labels:", [r.data() for r in s.run("CALL db.labels()")])
print("relTypes:", [r.data() for r in s.run("CALL db.relationshipTypes()")])
print("propKeys:", [r.data() for r in s.run("CALL db.propertyKeys()")])
print("indexes:", [r.data() for r in s.run("SHOW INDEXES")])Expected: the CALL db.* and SHOW INDEXES results return the schema of secret_db (type names, property keys, index and constraint definitions) even though limited_user has no grant on secret_db. A direct MATCH (n) RETURN n on the same session is blocked by the deny-all sentinel, confirming record data stays protected while schema metadata leaks.
Attack Chain
- Exposure: The Bolt protocol plugin is auto-registered via Java ServiceLoader and ships in the standard distribution;
BOLT_PORTdefaults to 7687 andBOLT_HOSTto0.0.0.0, so the endpoint is network-reachable by default. - Control: The attacker authenticates with valid credentials for a database they are granted, then fully controls the
dbfield of a subsequentRUNorBEGINmessage. Only emptiness is validated (db != null && !db.isEmpty()). - Path:
handleRun()/handleBegin()copy the attacker'sdbvalue intodatabaseName, then callensureDatabase(), which detects the switch, drops the current handle, and opens the named database viaserver.getDatabase(targetName). - Guard: No authorization guard exists on this path;
canAccessToDatabase()is never called (contrast MongoDB, Redis, PostgreSQL, gRPC, HTTP, all of which check). The deny-all sentinel that is bound only gates engine-routed record operations, not direct schema reads. - Primitive: The attacker issues
CALL db.labels(),CALL db.relationshipTypes(),CALL db.propertyKeys(),SHOW INDEXES, orSHOW CONSTRAINTS, whichhandleSystemQuery()answers by readingdatabase.getSchema()directly, before the ACL-gated engine path. - Result: The complete schema metadata of a database the attacker holds no grant for is serialized back as Bolt records.
Bypass Evidence
The relevant guard is the deny-all ServerSecurityDatabaseUser sentinel bound in ensureDatabase(). It is a real, effective control for record-level operations: requestAccessOnDatabase(), requestAccessOnFile(), and requestAccessOnType() all return false for the unauthorized database, so MATCH/CRUD through the engine is blocked. The bypass is that schema metadata reads do not travel that route. handleSystemQuery() returns before the engine's ACL-gated execution path, and the schema procedures read database.getSchema().getTypes() directly; LocalSchema.getTypes() returns the full type set unconditionally, and BoltSystemProcedures contains no security imports or permission checks. That the SQL path (FetchFromSchemaTypesStep) does call SecurityHelper.canAccessType() confirms the project intends schema reads to be authorization-filtered; the Bolt system-query path omits that filter.
Adversarial disproof attempts that failed: (1) server.getDatabase() performs no authorization; it is a plain map lookup. (2) Authentication does not re-check on database switch; ensureDatabase() contains no authenticate() or canAccessToDatabase() call. (3) The deny-all sentinel does not cover schema reads, as traced above. (4) Bolt is enabled by default, not an opt-in module. (5) Both RUN and BEGIN accept the db switch. Every other protocol handler checks canAccessToDatabase(); Bolt is the sole exception (grep -c canAccessToDatabase BoltNetworkExecutor.java = 0).
Affected Versions
Ecosystem: mavenPackage: com.arcadedb:arcadedb-boltConfirmed affected range: >= 26.2.1, <= 26.9.1Latest release checked: 26.9.1 (Maven Central / GitHub release tag)Fix status: not fixed (no fix on any released version or on the current development branch)
The Bolt module was introduced in 26.2.1; the vulnerable code is present unchanged from that release through the latest release, 26.9.1. The related cross-database authorization advisory GHSA-x8mg-6r4p-87pf was fixed in 26.7.2, but that fix targeted the HTTP handler family in com.arcadedb:arcadedb-server and never touched the Bolt module (git log --all --grep=GHSA-x8mg -- bolt/ returns nothing). The single commit that modified the Bolt executor between 26.7.2 and 26.9.1 added pre-authentication connection gating, not per-database authorization. No fixed release exists.
Suggested Fix
Enforce the same per-database authorization invariant on the Bolt path that the other protocols already apply. In ensureDatabase(), before opening a client-named database via server.getDatabase(targetName), verify user.canAccessToDatabase(targetName) and fail closed (returning a Bolt security failure) when the grant is absent, including the deferred-selection case where the database name is only supplied on a later RUN/BEGIN. Because the schema system-query path reads outside the engine ACL layer, the authorization check must gate database selection itself rather than relying on the deny-all sentinel; alternatively, route the handleSystemQuery() schema reads through the same SecurityHelper.canAccessType() filter the SQL path uses. As an interim mitigation without a code change, restrict Bolt port exposure to trusted networks and grant users only on servers where cross-database schema visibility is acceptable.
---
Reported by zx (GitHub: @manus-pi).
Text from GitHub Advisory Database
Affected versions 1
- com.arcadedb:arcadedb-bolt, maven>= 26.2.1, <= 26.9.1GitHub