Server: Budibase: OAuth2 Token Disclosure via Automation Test Results Broadcast to Other Builders (CVE-2026-73308)
## Summary When an SSO-authenticated user tests an automation in the Budibase builder, their OAuth2 access token and refresh token are included in the automation test results. These results are broadcast via WebSocket to all builders connected to the same dev app and stored in an in-memory cache accessible to any builder who polls the test status endpoint. This allows any co-builder of the same app to steal the testing user's OAuth2 tokens. ## Details The vulnerability exists because `getUserContextBindings()` intentionally includes OAuth2 tokens in user context bindings (so automations can call external APIs), but the automation test pipeline exposes the full result — including these tokens — to all builders of the same app without sanitization. **Step 1: Tokens included in user bindings** In `packages/server/src/sdk/users/utils.ts:134-161`: ```typescript export function getUserContextBindings(user: ContextUser): UserBindings { const bindings: UserBindings = { _id: user._id, email: user.email, // ... } if (isSSOUser(user) && user.oauth2) { bindings.oauth2 = { accessToken: user.oauth2.accessToken, // <-- sensitive refreshToken: user.oauth2.refreshToken, // <-- sensitive } } return bindings } ``` **Step 2: Bindings passed to automation execution** In `packages/server/src/api/controllers/automation.ts:311-312`: ```typescript const user = sdk.users.getUserContextBindings(ctx.user) return await triggers.externalTrigger( { ...automation, disabled: false }, { ...input, appId, user }, // user with tokens passed as event param { getResponses: true, onProgress: emitProgress } ) ``` **Step 3: Tokens placed in trigger outputs** In `packages/server/src/threads/automation.ts:409-413`: ```typescript const trigger: AutomationTriggerResult = { id: data.automation.definition.trigger.id, stepId: data.automation.definition.trigger.stepId, inputs: null, outputs: data.event, // data.event includes user.oauth2 tokens } ``` **Step 4: Result broadcast without sanitization** The full result (including `trigger.outputs.user.oauth2`) is exposed via two vectors: 1. **WebSocket broadcast** — `builderSocket.emitToRoom()` calls `this.io.in(room).emit()` (`packages/server/src/websockets/websocket.ts:291`) which sends to ALL sockets in the app's room, not just the originator. 2. **Test status endpoint** — `recordTestProgress()` stores the result in a Map keyed by `${appId}:${automationId}` with no user isolation (`packages/server/src/automations/testProgress.ts:41-74`). Any builder can call `GET /api/automations/:id/test/status` to retrieve another user's test results. ## PoC Requires two builder-level users on the same Budibase app, where User A authenticates via SSO/OIDC (Google, Azure AD, etc.) which provides OAuth2 tokens. ```bash # Step 1: User A (SSO-authenticated builder) tests an automation asynchronously curl -X POST http://localhost:10000/api/automations/<automation-id>/test?async=true \ -H 'x-budibase-app-id: app_dev_<appid>' \ -H 'Cookie: budibase:auth=<userA_session>' \ -H 'Content-Type: application/json' \ -d '{"row": {"tableId": "ta_xxx"}}' # Returns: {"message": "Automation test started"} # Step 2: User B (another builder on the same app) polls the test status curl -X GET http://localhost:10000/api/automations/<automation-id>/test/status \ -H 'x-budibase-app-id: app_dev_<appid>' \ -H 'Cookie: budibase:auth=<userB_session>' # Response includes the full automation result with: # result.trigger.outputs.user.oauth2.accessToken = "ya29.a0AfH6SM..." # result.trigger.outputs.user.oauth2.refreshToken = "1//0eXyz..." ``` Additionally, User B can passively receive the tokens by simply having the Budibase builder open (connected via WebSocket), as the `BuilderSocketEvent.AutomationTestProgress` event with `status: "complete"` includes the full result payload. ## Impact - **OAuth2 access tokens** for external services (Google Workspace, Azure AD, GitHub, etc.) are exposed to co-builders of the same app. These tokens can be used to access external APIs as the victim user. - **OAuth2 refresh tokens** provide persistent access — an attacker can generate new access tokens even after the original expires, maintaining long-term access to the victim's external service accounts. - The attack is passive via WebSocket — an attacker only needs to have the builder UI open to receive tokens when any co-builder tests an automation. - Test results persist in memory for 5 minutes (TTL in `testProgress.ts:15`), providing a window for polling-based attacks. ## Recommended Fix Strip OAuth2 tokens from automation test results before storing/broadcasting them. The tokens are needed during automation execution but should not be included in the result sent to clients. In `packages/server/src/api/controllers/automation.ts`, sanitize the result before passing to `emitProgress`: ```typescript function sanitizeAutomationResult(result: AutomationResults): AutomationR
AI Analysis
Technical Summary
In Budibase server (<=3.38.1), when an SSO-authenticated user tests an automation, their OAuth2 tokens are included in the user context bindings and passed through the automation execution pipeline. The full automation test result, including these tokens, is broadcast via WebSocket to all builders connected to the same app and stored in an in-memory cache keyed by app and automation IDs. This design flaw allows any co-builder of the same app to retrieve another user's OAuth2 access and refresh tokens either by listening to WebSocket events or by polling the test status endpoint. The tokens provide access to external APIs and persistent access via refresh tokens. The vulnerability is due to lack of sanitization of sensitive tokens before broadcasting or storing test results.
Potential Impact
OAuth2 access tokens and refresh tokens for external services (e.g., Google Workspace, Azure AD, GitHub) are exposed to any builder user connected to the same Budibase app. Attackers can use these tokens to access external APIs as the victim user and maintain long-term access by generating new access tokens from refresh tokens. The exposure is passive via WebSocket broadcasts and active via test status polling. The tokens remain accessible in memory for approximately 5 minutes, increasing the window for exploitation.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. The recommended fix is to sanitize automation test results by stripping OAuth2 tokens before broadcasting or storing them. Specifically, tokens should be removed from the results passed to the progress emission and test status endpoints. Until a patch is available, restrict builder user access to trusted users only and avoid testing automations with SSO-authenticated users in shared app environments.
Server: Budibase: OAuth2 Token Disclosure via Automation Test Results Broadcast to Other Builders (CVE-2026-73308)
Description
## Summary When an SSO-authenticated user tests an automation in the Budibase builder, their OAuth2 access token and refresh token are included in the automation test results. These results are broadcast via WebSocket to all builders connected to the same dev app and stored in an in-memory cache accessible to any builder who polls the test status endpoint. This allows any co-builder of the same app to steal the testing user's OAuth2 tokens. ## Details The vulnerability exists because `getUserContextBindings()` intentionally includes OAuth2 tokens in user context bindings (so automations can call external APIs), but the automation test pipeline exposes the full result — including these tokens — to all builders of the same app without sanitization. **Step 1: Tokens included in user bindings** In `packages/server/src/sdk/users/utils.ts:134-161`: ```typescript export function getUserContextBindings(user: ContextUser): UserBindings { const bindings: UserBindings = { _id: user._id, email: user.email, // ... } if (isSSOUser(user) && user.oauth2) { bindings.oauth2 = { accessToken: user.oauth2.accessToken, // <-- sensitive refreshToken: user.oauth2.refreshToken, // <-- sensitive } } return bindings } ``` **Step 2: Bindings passed to automation execution** In `packages/server/src/api/controllers/automation.ts:311-312`: ```typescript const user = sdk.users.getUserContextBindings(ctx.user) return await triggers.externalTrigger( { ...automation, disabled: false }, { ...input, appId, user }, // user with tokens passed as event param { getResponses: true, onProgress: emitProgress } ) ``` **Step 3: Tokens placed in trigger outputs** In `packages/server/src/threads/automation.ts:409-413`: ```typescript const trigger: AutomationTriggerResult = { id: data.automation.definition.trigger.id, stepId: data.automation.definition.trigger.stepId, inputs: null, outputs: data.event, // data.event includes user.oauth2 tokens } ``` **Step 4: Result broadcast without sanitization** The full result (including `trigger.outputs.user.oauth2`) is exposed via two vectors: 1. **WebSocket broadcast** — `builderSocket.emitToRoom()` calls `this.io.in(room).emit()` (`packages/server/src/websockets/websocket.ts:291`) which sends to ALL sockets in the app's room, not just the originator. 2. **Test status endpoint** — `recordTestProgress()` stores the result in a Map keyed by `${appId}:${automationId}` with no user isolation (`packages/server/src/automations/testProgress.ts:41-74`). Any builder can call `GET /api/automations/:id/test/status` to retrieve another user's test results. ## PoC Requires two builder-level users on the same Budibase app, where User A authenticates via SSO/OIDC (Google, Azure AD, etc.) which provides OAuth2 tokens. ```bash # Step 1: User A (SSO-authenticated builder) tests an automation asynchronously curl -X POST http://localhost:10000/api/automations/<automation-id>/test?async=true \ -H 'x-budibase-app-id: app_dev_<appid>' \ -H 'Cookie: budibase:auth=<userA_session>' \ -H 'Content-Type: application/json' \ -d '{"row": {"tableId": "ta_xxx"}}' # Returns: {"message": "Automation test started"} # Step 2: User B (another builder on the same app) polls the test status curl -X GET http://localhost:10000/api/automations/<automation-id>/test/status \ -H 'x-budibase-app-id: app_dev_<appid>' \ -H 'Cookie: budibase:auth=<userB_session>' # Response includes the full automation result with: # result.trigger.outputs.user.oauth2.accessToken = "ya29.a0AfH6SM..." # result.trigger.outputs.user.oauth2.refreshToken = "1//0eXyz..." ``` Additionally, User B can passively receive the tokens by simply having the Budibase builder open (connected via WebSocket), as the `BuilderSocketEvent.AutomationTestProgress` event with `status: "complete"` includes the full result payload. ## Impact - **OAuth2 access tokens** for external services (Google Workspace, Azure AD, GitHub, etc.) are exposed to co-builders of the same app. These tokens can be used to access external APIs as the victim user. - **OAuth2 refresh tokens** provide persistent access — an attacker can generate new access tokens even after the original expires, maintaining long-term access to the victim's external service accounts. - The attack is passive via WebSocket — an attacker only needs to have the builder UI open to receive tokens when any co-builder tests an automation. - Test results persist in memory for 5 minutes (TTL in `testProgress.ts:15`), providing a window for polling-based attacks. ## Recommended Fix Strip OAuth2 tokens from automation test results before storing/broadcasting them. The tokens are needed during automation execution but should not be included in the result sent to clients. In `packages/server/src/api/controllers/automation.ts`, sanitize the result before passing to `emitProgress`: ```typescript function sanitizeAutomationResult(result: AutomationResults): AutomationR
CVSS v3.1
Score 5.7medium
Affected software
Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
Weaknesses
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
In Budibase server (<=3.38.1), when an SSO-authenticated user tests an automation, their OAuth2 tokens are included in the user context bindings and passed through the automation execution pipeline. The full automation test result, including these tokens, is broadcast via WebSocket to all builders connected to the same app and stored in an in-memory cache keyed by app and automation IDs. This design flaw allows any co-builder of the same app to retrieve another user's OAuth2 access and refresh tokens either by listening to WebSocket events or by polling the test status endpoint. The tokens provide access to external APIs and persistent access via refresh tokens. The vulnerability is due to lack of sanitization of sensitive tokens before broadcasting or storing test results.
Potential Impact
OAuth2 access tokens and refresh tokens for external services (e.g., Google Workspace, Azure AD, GitHub) are exposed to any builder user connected to the same Budibase app. Attackers can use these tokens to access external APIs as the victim user and maintain long-term access by generating new access tokens from refresh tokens. The exposure is passive via WebSocket broadcasts and active via test status polling. The tokens remain accessible in memory for approximately 5 minutes, increasing the window for exploitation.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. The recommended fix is to sanitize automation test results by stripping OAuth2 tokens before broadcasting or storing them. Specifically, tokens should be removed from the results passed to the progress emission and test status endpoints. Until a patch is available, restrict builder user access to trusted users only and avoid testing automations with SSO-authenticated users in shared app environments.
Technical Details
- Gcve Source
- db.gcve.eu
- Osv Id
- GHSA-gh4h-34gr-87r7
- Osv Schema Version
- 1.4.0
- Aliases
- []
- Ecosystems
- ["npm"]
- Database Specific Severity
- MODERATE
- Cvss Version
- 3.1
Threat ID: 6a65422c9c2644c7f808a493
Added to database: 07/25/2026, 23:09:32 UTC
Last enriched: 07/25/2026, 23:56:30 UTC
Last updated: 09/08/2026, 05:37:25 UTC
Views: 80
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.