GitHub Advisoryadvisory · npm

GHSA-hr66-5mqr-8mpx Budibase: Unauthenticated user information disclosure via public tenant user lookup endpoint

HIGH Published 2026-07-24 Modified 2026-08-12 Official page ↗
Aliases
CVE-2026-73406
Ecosystem
npm
Packages
@budibase/server
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Weaknesses
CWE-200
Reviewed
2026-07-24

Affected packages

• npm @budibase/server: <= 3.38.1 (no fix yet)

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 ) => { 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" } Non-existent user: curl -s http://localhost:4002/api/global/users/tenant/[email protected] Response: 400 "No tenant user found." (Different response confirms user enumeration) ``` Negative case: Requesting a non-existent user returns HTTP 400 with `"No tenant user found."`, while an existing user returns HTTP 200 with full data. The different status codes confirm user existence, enabling enumeration. Impact This is a CWE-200: Exposure of Sensitive Information to an Unauthorized Actor vulnerability. Who is impacted: All Budibase deployments — both self-hosted and cloud. The impact is highest for multi-tenant (cloud) deployments where tenant IDs are security boundaries and user enumeration across tenants enables targeted attacks. An unauthenticated attacker can: 1. Enumerate all user accounts by testing known or guessed email addresses against the endpoint 2. Extract tenant IDs for any known user, enabling targeted cross-tenant attacks 3. Extract user IDs (`userId`) for use in other API calls or attacks 4. Extract SSO identifiers (`ssoId`) which may link to external identity providers (Google, OIDC) 5. Confirm user existence through different HTTP responses (200 vs 400) 6. Harvest CouchDB revision tokens (`_rev`) which could assist in CouchDB-level attacks The returned tenant IDs are particularly dangerous in multi-tenant deployments because they identify the security boundary between organizations. Combined with the hardcoded session keys (separate finding), an attacker could use enumerated tenant IDs to craft targeted session fixation attacks. Suggested remediation 1. Remove the endpoint from `PUBLIC_ENDPOINTS` and move it to internal-only routes, as the TODO comment at line 56 already suggests 2. Add authentication and authorization if the endpoint must remain accessible — require at least `builderOrAdmin` role 3. Limit returned fields to only what the consumer actually needs (strip `_rev`, `ssoId`, and other sensitive fields) 4. Return generic 404 for both "not found" and "access denied" to prevent user enumeration 5. Add rate limiting to prevent automated mass enumeration 6. Regression test: Add a test verifying `GET /api/global/users/tenant/:id` returns 403 without authentication

Related

Alias · CVE (1)

  • CVE-2026-73406 Budibase is an open-source low-code platform. vulnerability

Weakness · CWE (1)

  • CWE-200 Exposure of Sensitive Information to an Unauthorized Actor weakness

References