CVE-2026-73305
Budibase: Privilege escalation via public role assignment API missing app-level authorization
Budibase is an open-source low-code platform. Prior to 3.39.24, POST /api/public/v1/roles/assign called validateGlobalRoleUpdate without checking appBuilder.appId or role.appId in packages/server/src/api/controllers/public/globalRoleValidation.ts. An app-scoped builder could scope the request to an app they control and then grant themselves builder access or an arbitrary role in another app, exposing that app data, datasource configuration, and automations. This issue is fixed in version 3.39.24.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.8
Budibase: Privilege escalation via public role assignment API missing app-level authorization
Budibase is an open-source low-code platform. Prior to 3.39.24, POST /api/public/v1/roles/assign called validateGlobalRoleUpdate without checking appBuilder.appId or role.appId in packages/server/src/api/controllers/public/globalRoleValidation.ts. An app-scoped builder could scope the request to an app they control and then grant themselves builder access or an arbitrary role in another app, exposing that app data, datasource configuration, and automations. This issue is fixed in version 3.39.24.
- Record
CVE-2026-73305- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-269, CWE-862, CWE-863
- Updated
- 2026-11-02
EUVDEUVD-2026-58528
8.8
Budibase is an open-source low-code platform. Prior to 3.39.24, POST /api/public/v1/roles/assign called validateGlobalRoleUpdate without checking appBuilder.appId or role.appId in packages/server/src/api/controllers/public/globalRoleValidation.ts. An app-scoped builder could scope the request to an app they control and then grant themselves builder access or an arbitrary role in another app, exposing that app data, datasource configuration, and automations. This issue is fixed in version 3.39.24.
- Record
EUVD-2026-58528- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-j9fc-w3mr-x6mv
8.8
Budibase: Privilege escalation via public role assignment API missing app-level authorization
Summary
Budibase 3.39.19 (commit 03fbabae4) is affected by a privilege-escalation / missing-authorization flaw in the public role-assignment API. An app-scoped builder (a user who builds only specific apps — user.builder.apps = [appA], not a global builder or admin) can grant themselves builder access to ANY other app in the tenant, or assign themselves/any user an arbitrary data-plane role (e.g. ADMIN) in any app, by calling POST /api/public/v1/roles/assign. The endpoint only authorizes the two global flags (admin, builder); the per-app appBuilder and role:{appId,roleId} grant vectors are passed to the backend without any authorization check that the caller controls the target app. Reproduced in a local authorized lab using the verbatim authorization-gate and SDK logic.
This is an incomplete fix of the role-assignment hardening in commit d63d1d9054 ("Inline public user global role validation"), which only ever validated the global flags.
Details
The issue is caused by authorization being implemented as a flag-level allowlist (admin/builder) instead of validating the scope the caller is granting.
Relevant code paths:
packages/server/src/api/controllers/public/globalRoleValidation.ts—validateGlobalRoleUpdate(ctx, roleUpdate)only checksroleUpdate.admin(requiresisAdmin) androleUpdate.builder(requiresisGlobalBuilder). TheGlobalRoleUpdateinterface declares only{ builder?, admin? };appBuilderandroleare not referenced.packages/server/src/api/controllers/public/roles.ts—assign()doesconst { userIds, ...assignmentProps } = ctx.request.body; validateGlobalRoleUpdate(ctx, assignmentProps); await sdk.publicApi.roles.assign(userIds, assignmentProps). TheappBuilder/roleprops pass through unvalidated.packages/pro/src/sdk/publicApi/roles.ts—assign(): foropts.appBuilderit setsuser.builder = { apps: existing.concat([getProdWorkspaceID(opts.appBuilder.appId)]) }; foropts.roleit setsuser.roles[getProdWorkspaceID(opts.role.appId)] = opts.role.roleId. No check that the caller builds the target app, anduserIdsis an arbitrary list (bulkGet). The only gate is theisExpandedPublicApiEnabled()license check.packages/server/src/api/routes/public/index.ts—applyAdminRoutes(roleEndpoints)attaches onlymiddleware.builderOrAdmin(nopublicApi, noauthorized(PermissionType.USER, …)).packages/backend-core/src/middleware/builderOrAdmin.ts— with aworkspaceIdpresent, it only requiresisBuilder(ctx.user, workspaceId). The attacker setsx-budibase-app-idto their own app (appA), so the gate passes.packages/backend-core/src/middleware/builderOnly.ts— on the worker,POST /api/global/self/api_keyonly requireshasBuilderPermissions(ctx.user), which is true for app-scoped builders, so the attacker can self-issue a public API key.packages/shared-core/src/sdk/documents/users.ts—isGlobalBuilderis false for app-scoped builders (so the globalbuilderflag is correctly blocked), whileisBuilder(user, appA)andhasBuilderPermissions(user)are true.
Attack flow:
- Attacker is an app-scoped builder of
appAonly (no global builder/admin). POST /api/global/self/api_key(worker) — passesbuilderOnlyviahasBuilderPermissions→ attacker obtains a public API key.POST /api/public/v1/roles/assignwith headerx-budibase-app-id: <appA prod id>and body{"userIds":["<self>"],"appBuilder":{"appId":"<appB>"}}—builderOrAdminpasses (isBuilder(user, appA)),validateGlobalRoleUpdateignoresappBuilder, the SDK pushesappBintouser.builder.apps.- Attacker is now a builder of
appB(and, via therolevector, can set any data-role such asADMINin any app).
Security boundary crossed:
- Before: builder of
appAonly. - After: builder of
appB(and any other app) → read/modify all rows, read datasource configs…
- Record
GHSA-j9fc-w3mr-x6mv- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-269, CWE-862, CWE-863
- Updated
- 2026-11-02
NVD
8.8
Budibase is an open-source low-code platform. Prior to 3.39.24, POST /api/public/v1/roles/assign called validateGlobalRoleUpdate without checking appBuilder.appId or role.appId in packages/server/src/api/controllers/public/globalRoleValidation.ts. An app-scoped builder could scope the request to an app they control and then grant themselves builder access or an arbitrary role in another app, exposing that app data, datasource configuration, and automations. This issue is fixed in version 3.39.24.
- Record
CVE-2026-73305- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-269, CWE-862, CWE-863
- Updated
- 2026-11-02
OSV
8.8
Budibase: Privilege escalation via public role assignment API missing app-level authorization
Budibase is an open-source low-code platform. Prior to 3.39.24, POST /api/public/v1/roles/assign called validateGlobalRoleUpdate without checking appBuilder.appId or role.appId in packages/server/src/api/controllers/public/globalRoleValidation.ts. An app-scoped builder could scope the request to an app they control and then grant themselves builder access or an arbitrary role in another app, exposing that app data, datasource configuration, and automations. This issue is fixed in version 3.39.24.
- Record
CVE-2026-73305- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-269, CWE-862, CWE-863
- Updated
- 2026-11-02
OSVGHSA-j9fc-w3mr-x6mv
8.8
Budibase: Privilege escalation via public role assignment API missing app-level authorization
Summary
Budibase 3.39.19 (commit 03fbabae4) is affected by a privilege-escalation / missing-authorization flaw in the public role-assignment API. An app-scoped builder (a user who builds only specific apps — user.builder.apps = [appA], not a global builder or admin) can grant themselves builder access to ANY other app in the tenant, or assign themselves/any user an arbitrary data-plane role (e.g. ADMIN) in any app, by calling POST /api/public/v1/roles/assign. The endpoint only authorizes the two global flags (admin, builder); the per-app appBuilder and role:{appId,roleId} grant vectors are passed to the backend without any authorization check that the caller controls the target app. Reproduced in a local authorized lab using the verbatim authorization-gate and SDK logic.
This is an incomplete fix of the role-assignment hardening in commit d63d1d9054 ("Inline public user global role validation"), which only ever validated the global flags.
Details
The issue is caused by authorization being implemented as a flag-level allowlist (admin/builder) instead of validating the scope the caller is granting.
Relevant code paths:
packages/server/src/api/controllers/public/globalRoleValidation.ts—validateGlobalRoleUpdate(ctx, roleUpdate)only checksroleUpdate.admin(requiresisAdmin) androleUpdate.builder(requiresisGlobalBuilder). TheGlobalRoleUpdateinterface declares only{ builder?, admin? };appBuilderandroleare not referenced.packages/server/src/api/controllers/public/roles.ts—assign()doesconst { userIds, ...assignmentProps } = ctx.request.body; validateGlobalRoleUpdate(ctx, assignmentProps); await sdk.publicApi.roles.assign(userIds, assignmentProps). TheappBuilder/roleprops pass through unvalidated.packages/pro/src/sdk/publicApi/roles.ts—assign(): foropts.appBuilderit setsuser.builder = { apps: existing.concat([getProdWorkspaceID(opts.appBuilder.appId)]) }; foropts.roleit setsuser.roles[getProdWorkspaceID(opts.role.appId)] = opts.role.roleId. No check that the caller builds the target app, anduserIdsis an arbitrary list (bulkGet). The only gate is theisExpandedPublicApiEnabled()license check.packages/server/src/api/routes/public/index.ts—applyAdminRoutes(roleEndpoints)attaches onlymiddleware.builderOrAdmin(nopublicApi, noauthorized(PermissionType.USER, …)).packages/backend-core/src/middleware/builderOrAdmin.ts— with aworkspaceIdpresent, it only requiresisBuilder(ctx.user, workspaceId). The attacker setsx-budibase-app-idto their own app (appA), so the gate passes.packages/backend-core/src/middleware/builderOnly.ts— on the worker,POST /api/global/self/api_keyonly requireshasBuilderPermissions(ctx.user), which is true for app-scoped builders, so the attacker can self-issue a public API key.packages/shared-core/src/sdk/documents/users.ts—isGlobalBuilderis false for app-scoped builders (so the globalbuilderflag is correctly blocked), whileisBuilder(user, appA)andhasBuilderPermissions(user)are true.
Attack flow:
- Attacker is an app-scoped builder of
appAonly (no global builder/admin). POST /api/global/self/api_key(worker) — passesbuilderOnlyviahasBuilderPermissions→ attacker obtains a public API key.POST /api/public/v1/roles/assignwith headerx-budibase-app-id: <appA prod id>and body{"userIds":["<self>"],"appBuilder":{"appId":"<appB>"}}—builderOrAdminpasses (isBuilder(user, appA)),validateGlobalRoleUpdateignoresappBuilder, the SDK pushesappBintouser.builder.apps.- Attacker is now a builder of
appB(and, via therolevector, can set any data-role such asADMINin any app).
Security boundary crossed:
- Before: builder of
appAonly. - After: builder of
appB(and any other app) → read/modify all rows, read datasource configs…
- Record
GHSA-j9fc-w3mr-x6mv- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-269, CWE-862, CWE-863
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
@budibase/servernpm |
through 3.38.1 | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-73305 is a high vulnerability (CVSS 8.8) in Budibase, published July 24, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.52%, the 44th percentile. No source names a fixed version.
References
github.com/Budibase/budibase/security/advisories/GHSA-j9fc-w3mr-x6mv
github.com/Budibase/budibase/pull/19098
github.com/Budibase/budibase/commit/453391d3245dd04cc386ccd6959d38ccf30db9a8
github.com/Budibase/budibase/releases/tag/3.39.24
github.com/Budibase/budibase/releases/tag/3.40.0
github.com/advisories/GHSA-j9fc-w3mr-x6mv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/73xxx/CVE-2026-73305.json
nvd.nist.gov/vuln/detail/CVE-2026-73305
github.com/Budibase/budibase
Left out here: the rest of 2 source descriptions. The JSON record has them.