GHSA-8783-3wgf-jggf Budibase: Authentication Bypass via Unanchored Regex in Public Endpoint Matcher — Unauthenticated Access to Protected Endpoints
SummaryThe `authenticated` middleware uses unanchored regular expressions to match public (no-auth) endpoint patterns against `ctx.request.url`. Since `ctx.request.url` in Koa includes the query string, an attacker can access any protected endpoint by appending a public endpoint path as a query parameter. For example, `POST /api/global/users/search?x=/api/system/status` bypasses all authentication because the regex `/api/system/status/` matches in the query string portion of the URL.Details**Step 1 — Public endpoint patterns compiled without anchors**`packages/backend-core/src/middleware/matchers.ts`, line 26:No `^` prefix, no `$` suffix. The regex matches anywhere in the test string.**Step 2 — Regex tested against full URL including query string**`packages/backend-core/src/middleware/matchers.ts`, line 32:Koa's `ctx.request.url` returns the full URL including query string (e.g., `/api/global/users/search?x=/api/system/status`). The regex `/api/system/status` matches in the query string.**Step 3 — publicEndpoint flag set to true**`packages/backend-core/src/middleware/authenticated.ts`, lines 123-125:**Step 4 — Worker's global auth check skipped**`packages/worker/src/api/index.ts`, lines 160-162:When `ctx.publicEndpoint` is `true`, the 403 check at line 165-168 is never executed.**Step 5 — Routes without per-route auth middleware are exposed**`loggedInRoutes` in `packages/worker/src/api/routes/endpointGroups/standard.ts` line 23:Endpoints on `loggedInRoutes` have NO secondary auth check. The global check at `index.ts:160-169` was their only protection.**Affected endpoints (no per-route auth — fully exposed):**`POST /api/global/users/search` — search all users (emails, names, roles)`GET /api/global/self` — get current user info`GET /api/global/users/accountholder` — account holder lookup`GET /api/global/template/definitions` — template definitions`POST /api/global/license/refresh` — refresh license`POST /api/global/event/publish` — publish events**Not affected (have secondary per-route auth that blocks undefined user):**`GET /api/global/users` — on `builderOrAdminRoutes` which checks `isAdmin(ctx.user)` → returns false for undefined → throws 403`DELETE /api/global/users/:id` — on `adminRoutes` → same secondary check blocks itPoCWithout auth → 403. With `?x=/api/system/status` → returns all users.Any public endpoint pattern works as the bypass value:`?x=/api/system/status``?x=/api/system/environment``?x=/api/global/configs/public``?x=/api/global/auth/default`ImpactAn unauthenticated attacker can:**Enumerate all users** — emails, names, roles, admin status, builder status via `/api/global/users/search`**Discover account holder** — identify the instance owner via `/api/global/users/accountholder`**Trigger license refresh** — potentially disrupt service via `/api/global/license/refresh`**Publish events** — inject events into the event system via `/api/global/event/publish`The user search is the most damaging — it reveals the full user directory of the Budibase instance to anyone on the internet.Note: endpoints on `builderOrAdminRoutes` and `adminRoutes` are NOT affected because they have secondary middleware (`workspaceBuilderOrAdmin`, `adminOnly`) that independently checks `ctx.user` and throws 403 when it's undefined. Only `loggedInRoutes` endpoints (which rely solely on the global auth check) are exposed.Suggested FixTwo options (both should be applied):**Option A — Anchor the regex:****Option B — Use ctx.request.path instead of ctx.request.url:**
CVSS v3.1 score: 9.1