Media

Budibase: OAuth2 Token Disclosure via Automation Test Results Broadcast to Other Builders

  • Publicada: 24.07.2026
  • Actualizado: 11.10.2026 10:22

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:

ts
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:

ts
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:

ts
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:

ts
function sanitizeAutomationResult(result: AutomationResults): AutomationResults {
  const sanitized = cloneDeep(result)
  if (sanitized.trigger?.outputs?.user?.oauth2) {
    delete sanitized.trigger.outputs.user.oauth2
  }
  for (const step of sanitized.steps || []) {
    if (step.outputs?.user?.oauth2) {
      delete step.outputs.user.oauth2
    }
  }
  return sanitized
}

Apply sanitization in the emitProgress callback at line 290 and before returning ctx.body at line 342:

ts
emitProgress({
  status: "complete",
  occurredAt: Date.now(),
  result: sanitizeAutomationResult(result),
})

Additionally, the testProgress store should be scoped per-user (keyed by ${appId}:${automationId}:${userId}) so that one builder's test results are not accessible to another builder via the testStatus endpoint.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de npm @budibase/server <= 3.38.1 sin corrección

Referencias

Puntuación CVSS 5.7/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N
Debilidad (CWE)
CWE-200
Plataforma
npm
Paquete
@budibase/server
Publicada
24.07.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-gh4h-34gr-87r7 - CC BY 4.0

Historial de vulnerabilidades: @budibase/server

Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (42) en el archivo
Entradas en total
42
Primera → última
03.2024 08.2026
Sin corrección
19
Explotada en ataques
0

Distribución por gravedad

  • Crítica 8
  • Alta 27
  • Media 7

Divulgaciones por año

  1. 2024: 1
  2. 2025: 0
  3. 2026: 41
Gravedad:
  1. Alta · 8.5 CVE-2026-73410 <= 3.38.1 sin corrección
  2. Alta · 7.7 CVE-2026-72849 <= 3.38.1 sin corrección
  3. Budibase: OAuth2 Token Disclosure via Automation Test Results Broadcast to Other Builders esta entrada
    Media · 5.7 CVE-2026-73308 <= 3.38.1 sin corrección
  4. Crítica · 9.6 CVE-2026-73300 <= 3.38.1 sin corrección
Mostrar más (16) Mostrando 20 de 36

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.