Chartbrew: Arbitrary File Deletion as Root via Data Connection SSL/SSH Path Fields

Chartbrew's connection removal calls fs.unlink on four user-controllable path fields with no restriction. An authenticated attacker sets an absolute path in an SSL or SSH field, deletes the connection, and removes any file on the server including .env and application source code.

Advisory ID: TP-2026-058
Product: Chartbrew (open-source business-intelligence platform for data visualization, self-hosted)
Vulnerability type: External Control of File Name or Path (CWE-73)
CVE: CVE-2026-85736
CVSS 3.1: 8.1 (High) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Vendor advisory: GHSA-p5h6-f969-jpfv
Affected versions: <= 5.2.2
Fixed in: 5.2.3
Reported: 16 July 2026

Summary

Chartbrew is a self-hosted open-source business-intelligence platform for data visualization whose removeConnection function calls fs.unlink on four path fields (sslCa, sslCert, sslKey, sshPrivateKey) with no path restriction when a data connection is deleted (server/controllers/ConnectionController.js:159-176). The create and update endpoints set those four columns straight from the request body: sanitizeConnectionWriteData strips only allowPrivateHost and empty SSH secrets, not the path fields (ConnectionController.js:9-22). sslCa, sslCert, and sslKey are plain string columns; sshPrivateKey is an encrypted column that round-trips the attacker's path. Every registered user is teamOwner of their own team and holds create and delete permissions on their own connections. Since the container runs as root, an authenticated attacker deletes any file on the server.

Root cause

removeConnection unconditionally runs fs.unlink on connection.sslCa, sslCert, sslKey, and sshPrivateKey on every connection delete (server/controllers/ConnectionController.js:159-176). sanitizeConnectionWriteData filters only allowPrivateHost and empty SSH secrets, leaving the four path fields mass-assignable from the request body (ConnectionController.js:9-22). sslCa, sslCert, and sslKey are plain STRING columns, and sshPrivateKey's setter and getter round-trip the raw path, so POST and PUT /team/:team_id/connections store an attacker-chosen absolute path verbatim. POST requires createOwn and DELETE requires deleteOwn, both held by the teamOwner role every self-signup gets on its own team. The container process runs as uid 0, so the unlink targets any file including /code/.env and application source.

Proof of Concept

# Create a connection whose sslCa points at a target file:
POST /team/<T>/connections HTTP/1.1
Authorization: Bearer <user>
{"name":"x","type":"mysql","team_id":<T>,"sslCa":"/tmp/marker.txt"}

HTTP/1.1 200 OK
{"id":<C>,"sslCa":"/tmp/marker.txt", ...}

# Delete the connection to trigger the unlink:
DELETE /team/<T>/connections/<C> HTTP/1.1
Authorization: Bearer <user>

HTTP/1.1 200 OK
{"removed":true}

# /tmp/marker.txt is gone.
# The encrypted sshPrivateKey field reaches the same unlink;
# setting it to an application source path deletes that file.

Impact

  • Arbitrary file deletion as root: integrity and availability compromise.
  • Deletion of /code/.env (encryption key and database credentials) or application source breaks the instance.
  • Lowest legitimate role on default config; duplicateConnection provides a second trigger.

References

Is Something Like This in Your Software?

Our team discovered this vulnerability during its work. Have your applications reviewed by the same specialists, with a penetration test from turingpoint.