Summary
On an ArcadeDB HA cluster, the Raft cluster-info endpoints authenticate but do not authorize: they enumerate the full server database registry and return per-database metadata (names, bootstrap fingerprints, last transaction ids) plus peer/leader topology to ANY authenticated user, including one granted access to only one database or none. This is a cross-database information-disclosure IDOR.
Root Cause
GetClusterHandler.execute and PostBootstrapStateHandler.execute perform authentication but contain no checkRootUser, canAccessToDatabase, or getAuthorizedDatabases call (grep = 0), unlike every mutating Raft sibling (PostAddPeer, PostTransferLeader, PostStepDown, PostLeave, DeletePeer, PostResyncDatabase, PostVerifyDatabase) which call checkRootUser. Both read handlers loop httpServer.getServer().getDatabaseNames() (the full registry) and emit per-database metadata (GetClusterHandler.java:131; PostBootstrapStateHandler emits fingerprints). The canonical GetDatabasesHandler proves the intended design: it does installedDatabases.retainAll(allowedDatabases) — the cluster handlers omit that filter. GetServerHandler?mode=cluster similarly leaks db names + quorum + peer/leader addresses on any HA server.
Impact
Authenticated cross-database reconnaissance: a tenant scoped to one database learns the names, transaction ids, and (on the ha-raft path) directory fingerprints of all other tenants' databases plus cluster topology. Metadata-only confidentiality impact.
Proof of Concept
# alice granted only db 'foo' (or zero dbs)
GET /api/v1/cluster -> databases[] with finance/hr names, txids, peer/leader addrs
POST /api/v1/cluster/bootstrap-state {} -> per-db SHA-256 fingerprints across peers
Attack Chain
- Entry: authenticate as low-priv
alice; GET /api/v1/cluster. Guard: AbstractServerHttpHandler.handleRequest requires a valid user (:176-183). Bypass proof: alice's valid creds pass; execute() calls no authz gate (GetClusterHandler.java:65-161, grep = 0 authz calls).
- Read
databases[]. Guard: none — loop over getDatabaseNames() (:131) with no retainAll(getAuthorizedDatabases()). Bypass proof: foo-only alice receives finance/hr names + txids + peer addresses.
Bypass Evidence
GetClusterHandler.java:131 and :208 iterate getDatabaseNames(); no canAccessToDatabase/getAuthorizedDatabases/checkRootUser in the handler (verified on 26.7.3). Not covered by GHSA-x8mg (guards named-db-in-path handlers) nor GHSA-46hj (redacted clusterToken via isHidden in exportSettings).
Affected Versions
<= 26.7.3. Reachable only when HA is enabled (arcadedb.ha.enabled, default false) and the ha-raft module is loaded; GetServerHandler?mode=cluster leaks names/quorum/addresses on any HA server.
Suggested Fix
Apply the GetDatabasesHandler pattern — retainAll(user.getAuthorizedDatabases()) — to the cluster handlers, or restrict the cluster-info endpoints to root (matching the mutating Raft siblings).
Reported by zx (Jace) — GitHub: @manus-use
Summary
On an ArcadeDB HA cluster, the Raft cluster-info endpoints authenticate but do not authorize: they enumerate the full server database registry and return per-database metadata (names, bootstrap fingerprints, last transaction ids) plus peer/leader topology to ANY authenticated user, including one granted access to only one database or none. This is a cross-database information-disclosure IDOR.
Root Cause
GetClusterHandler.executeandPostBootstrapStateHandler.executeperform authentication but contain nocheckRootUser,canAccessToDatabase, orgetAuthorizedDatabasescall (grep = 0), unlike every mutating Raft sibling (PostAddPeer, PostTransferLeader, PostStepDown, PostLeave, DeletePeer, PostResyncDatabase, PostVerifyDatabase) which callcheckRootUser. Both read handlers loophttpServer.getServer().getDatabaseNames()(the full registry) and emit per-database metadata (GetClusterHandler.java:131; PostBootstrapStateHandler emits fingerprints). The canonicalGetDatabasesHandlerproves the intended design: it doesinstalledDatabases.retainAll(allowedDatabases)— the cluster handlers omit that filter.GetServerHandler?mode=clustersimilarly leaks db names + quorum + peer/leader addresses on any HA server.Impact
Authenticated cross-database reconnaissance: a tenant scoped to one database learns the names, transaction ids, and (on the ha-raft path) directory fingerprints of all other tenants' databases plus cluster topology. Metadata-only confidentiality impact.
Proof of Concept
Attack Chain
alice;GET /api/v1/cluster. Guard:AbstractServerHttpHandler.handleRequestrequires a valid user (:176-183). Bypass proof: alice's valid creds pass;execute()calls no authz gate (GetClusterHandler.java:65-161, grep = 0 authz calls).databases[]. Guard: none — loop overgetDatabaseNames()(:131) with noretainAll(getAuthorizedDatabases()). Bypass proof: foo-only alice receives finance/hr names + txids + peer addresses.Bypass Evidence
GetClusterHandler.java:131and:208iterategetDatabaseNames(); nocanAccessToDatabase/getAuthorizedDatabases/checkRootUserin the handler (verified on 26.7.3). Not covered by GHSA-x8mg (guards named-db-in-path handlers) nor GHSA-46hj (redacted clusterToken via isHidden in exportSettings).Affected Versions
<= 26.7.3. Reachable only when HA is enabled (arcadedb.ha.enabled, default false) and the ha-raft module is loaded;GetServerHandler?mode=clusterleaks names/quorum/addresses on any HA server.Suggested Fix
Apply the
GetDatabasesHandlerpattern —retainAll(user.getAuthorizedDatabases())— to the cluster handlers, or restrict the cluster-info endpoints to root (matching the mutating Raft siblings).Reported by zx (Jace) — GitHub: @manus-use