Loading...
Skip to main content

CVE-2026-54352

CRITICAL

Budibase has arbitrary file read by workspace-builder via PWA-zip symlink upload

Published June 22, 2026Updated August 12, 2026Source: osv

Summary

## Summary `POST /api/pwa/process-zip` at `packages/server/src/api/routes/static.ts:24` accepts a builder-uploaded `.zip`, extracts it with `extract-zip@2.0.1` into a temp directory, then for each entry listed in `icons.json` validates the icon path, opens it, and streams the bytes into MinIO. The resulting object is served back via `GET /api/assets/{appId}/pwa/{uuid}.png`. `extract-zip@2.0.1` preserves absolute symlink targets when restoring symlink entries. The icon-source validator at `packages/server/src/api/controllers/static/index.ts:259-268` resolves the icon source string against `baseDir` (`path.resolve`), checks `resolvedSrc.startsWith(baseDir + path.sep)` against that string, and calls `fs.existsSync(resolvedSrc)` which follows symbolic links to confirm the target exists. None of the three calls reject symbolic-link entries, so an entry stored at `baseDir/evil.png` but pointing at `/data/.env` passes the gate. `packages/backend-core/src/objectStore/objectStore.ts:302` then calls `(await fsp.open(path)).createReadStream()` on the resolved path. `fsp.open` follows the symlink, the target file's bytes stream into MinIO, and the response of the asset-fetch endpoint returns those bytes verbatim. Result: a workspace-level builder reads any file the server process can open (root inside the default Docker image, including `/data/.env` with `JWT_SECRET`, `INTERNAL_API_KEY`, `MINIO_*`, `REDIS_PASSWORD`, `COUCHDB_PASSWORD`, `DATABASE_URL`) by uploading one crafted PWA zip. ## Affected `Budibase/budibase` server, `@budibase/server` package, `<= 3.39.0` (HEAD `feab995`, released 2026-05-20). Reachable in stock self-hosted deployments. The default `budibase/budibase:latest` Docker image runs the Node server as `root` inside the container; the server process opens `/etc/passwd`, `/etc/shadow`, `/data/.env`, and every other root-readable file. Reachable from any account with the workspace-builder permission on at least one app. Not affected: managed cloud-hosted Budibase tenants where the file-system root is sandboxed away from secret material. ## Root cause `packages/server/src/api/routes/static.ts:24`: `.post("/api/pwa/process-zip", authorized(BUILDER), controller.processPWAZip)` exposes the endpoint to any workspace builder; the only permission required is `BUILDER`. `packages/server/src/api/controllers/static/index.ts:235`: `await extract(filePath, { dir: tempDir })` calls `extract-zip@2.0.1`, which preserves absolute symlink targets when restoring symlink entries. `packages/server/src/api/controllers/static/index.ts:259-268`: the icon validator (`path.resolve` + `resolvedSrc.startsWith(baseDir + path.sep)` + `fs.existsSync`) operates on the resolved string path and on `fs.existsSync` (which follows symbolic links). A symlink stored under `baseDir` whose target points anywhere reachable by the server passes the gate as long as the target exists. `packages/backend-core/src/objectStore/objectStore.ts:302`: `(await fsp.open(path)).createReadStream()` follows the symlink and streams the target file's bytes; the object lands in MinIO under `{appId}/pwa/{uuid}{extension}` and is served by `GET /api/assets/{appId}/pwa/{uuid}.{ext}` (`packages/server/src/api/routes/static.ts:21`). `hosting/single/Dockerfile`: the production single-container image runs the Node server as `root`, so the read primitive reaches `/etc/shadow`, `/data/.env`, and every other root-readable path. ## Reproduction `budibase/budibase:latest` (`v3.39.0`) Docker single-container on `localhost:10000`, default config, with any workspace builder logged in. Cookie jar and `<CSRF>` token come from `GET /api/global/self`. 1. Builder uploads a zip containing one symlink entry that targets `/data/.env`, plus an `icons.json` that references the symlink. ```bash mkdir attack && cd attack ln -s /data/.env evil.png printf '{"name":"x","icons":[{"src":"evil.png","sizes":"192x192","type":"image/png"}]}' > icons.json zip -y attack.zip icons.json evil.png curl -s "http://localhost:10000/api/pwa/process-zip" \ -b cookies.txt \ -H "x-budibase-app-id: <appId>" \ -H "x-csrf-token: <CSRF>" \ -F "file=@attack.zip" ``` ```json {"icons":[{"src":"<appId>/pwa/c9370128-885a-48bc-bd1c-5522f4c8020f.png","sizes":"192x192","type":"image/png"}]} ``` 2. Builder fetches the resulting "icon". ```http GET /api/assets/<appId>/pwa/c9370128-885a-48bc-bd1c-5522f4c8020f.png HTTP/1.1 Host: localhost:10000 Cookie: budibase:auth=<JWT>; budibase:auth.sig=<SIG> ``` ``` COUCHDB_USER=admin COUCHDB_PASSWORD=admin MINIO_ACCESS_KEY=bd501fa31bf44a7e8beb6f7b628c6def MINIO_SECRET_KEY=bf754d8f29434fc997225e10f55de778 INTERNAL_API_KEY=e9580f58b18b4371868aa3442c57522c JWT_SECRET=c5441dc903f845bdb93a98b949a612b2 REDIS_PASSWORD=50739fb539504149a5fd85c85fe6750c DATABASE_URL=postgresql://llmproxy:...@127.0.0.1:5432/litellm ``` Live-verified: the response body of the asset-fetch endpoint is byte-identical to `docker exec budibase cat /data/.env`; `/etc/passwd` and `/etc/shadow` extract via the same primitive when their permissions allow root reads. ## Impact - Disclosure of `/data/.env`: `JWT_SECRET`, `INTERNAL_API_KEY`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, `REDIS_PASSWORD`, `COUCHDB_PASSWORD`, `LITELLM_MASTER_KEY`, `DATABASE_URL`. - HS256 JWT forge with the leaked `JWT_SECRET` against any user id, including the global admin: scope-changing escalation from workspace-builder to global-admin. - Cross-tenant exposure on multi-tenant installs once the global-admin forge succeeds. - Disclosure of `/etc/passwd` and `/etc/shadow` via the same primitive when the container runs as `root` (the shipped default). ## Credit Jan Kahmen, [turingpoint](https://turingpoint.de) (jan@turingpoint.de).

Remediation

Upgrade to the fixed version using your package manager.

npm
Update @budibase/server to 3.39.9 or later
npm install @budibase/server@3.39.9

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

PackageEcosystemAffectedFixed In
@budibase/server
npm
All versions3.39.9

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 9.6 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N

Frequently Asked Questions

What is CVE-2026-54352?
Budibase has arbitrary file read by workspace-builder via PWA-zip symlink upload This vulnerability has been assigned a severity rating of CRITICAL (CVSS score: 9.6/10).
How do I check if my project is affected by CVE-2026-54352?
CVE-2026-54352 affects @budibase/server. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-54352 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
9.6

Exploitation is straightforward and causes maximum impact. Patch immediately.

Also Known As

GHSA-w7mq-r738-x278

Related CVEs

  • CVE-2026-48150
    CRITICAL

    Budibase: Workspace-scoped builder escalates to global admin via /api/public/v1/roles/assign

  • CVE-2026-54350
    CRITICAL

    Budibase has nonymous NoSQL operator injection via published-app query templates

  • CVE-2026-35216
    CRITICAL

    Budibase: Unauthenticated Remote Code Execution via Webhook Trigger and Bash Automation Step

  • CVE-2026-73302
    CRITICAL

    Budibase: OIDC SSO account takeover: incoming identity linked by email without checking email_verified

  • CVE-2026-35214
    HIGH

    Budibase: Path traversal in plugin file upload enables arbitrary directory deletion and file write

  • CVE-2026-50137
    HIGH

    Budibase: POST /api/attachments/:datasourceId/url is unauthenticated and lets anonymous callers mint S3 PUT pre-signed URLs using stored datasource IAM credentials

  • CVE-2026-45717
    HIGH

    Budibase: `PUT /api/datasources/:datasourceId` is protected only by `TABLE/READ` permission instead of builder access, allowing any authenticated app user to overwrite datasource connection parameters including host, port, and URL

  • CVE-2026-25044
    HIGH

    Budibase: Command Injection in Bash Automation Step

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies