A AegiFlow
HIGHCVSS 7.7

GHSA-pvcr-8mvp-w8qr

Budibase: Chat-Link Handoff Identity Confusion (Same-Tenant Account-Link CSRF)

Published
2026-07-24
Modified
2026-07-24
Sources
github-advisory

Summary

### Summary The Budibase AI chat-link handoff flow (`GET/POST /api/chat-links/:instance/:token/handoff`) binds an **external chat identity** (Slack/Discord/MS Teams/Telegram) to a **Budibase user account**. The confirmation endpoint is on a **public route** (no CSRF middleware, no auth-group gate) and the only credential it checks is a `confirmationToken` that is **already rendered in plaintext into the HTML confirmation page** the victim views. There is no binding between the confirmation token and the requester's Budibase session at preparation time, and no CSRF token on the POST. Consequently, an attacker who creates a chat-link session for **their own** external chat identity (or any identity they can mint in their chat platform) can induce a victim Budibase user (same tenant) to submit the confirmation POST -> for example by sending them a link that auto-submits, or by XSS/CSRF on a co-tenanted page -> and the victim's `globalUserId` becomes bound to the attacker's external identity. The attacker then sends messages to the AI agent from their chat platform and is **acting as the victim user** inside Budibase automations/agent operations, inheriting the victim's permissions on agent operations, knowledge sources, and any downstream automation steps keyed off the linked identity. --- ### Affected | Component | Path | Lines | |---|---|---| | Public handoff routes (no auth-group middleware) | `packages/server/src/api/routes/chat.ts` | `23` (`GET /api/chat-links/:instance/:token/handoff`), `27` (`POST .../handoff`) | | Confirmation controller | `packages/server/src/api/controllers/ai/chatIdentityLinks.ts` | `124-177` (`confirmChatLinkSession`); the binding at `153-173` | | Confirmation token rendered into HTML | `packages/server/src/api/controllers/ai/chatIdentityLinks.ts` | `40-60` (`renderLinkConfirmationPage`); the hidden input at `55` | | Session creation (attacker side) | `packages/server/src/sdk/workspace/ai/chatIdentityLinks.ts` | `138-171` (`createChatIdentityLinkSession`), `173-188` (`prepareChatIdentityLinkSessionConfirmation`) | | Upsert of identity link | `packages/server/src/sdk/workspace/ai/chatIdentityLinks.ts` | `201-250` (`upsertChatIdentityLink`) | **Affected versions:** `master` at commit `3c8d1b4023`. **Reachable over HTTP by:** the GET and POST are on `publicRoutes` (no auth-group middleware). The POST requires `ctx.isAuthenticated` at runtime (`controllers/ai/chatIdentityLinks.ts:142`) -> so the victim must be logged into Budibase when the POST fires (achievable via standard CSRF if cookies are sent cross-origin, or via phishing that lures the victim to submit). --- ### Root cause **Issue 1 -> The handoff routes are public and unauthenticated at the middleware layer.** ```ts // packages/server/src/api/routes/chat.ts:23, 27 publicRoutes.get("/api/chat-links/:instance/:token/handoff", ai.handoffChatLinkSession) publicRoutes.post("/api/chat-links/:instance/:token/handoff", ai.confirmChatLinkSession) ``` `publicRoutes` has **no group middleware** (`endpointGroups/standard.ts:24-25` calls `endpointGroupList.group()` with no middleware and then `.lockMiddleware()`). The routes do not have a per-route `authorized(...)`. There is **no CSRF middleware** on these routes -> Budibase's CSRF synchroniser token is enforced only for state-changing verbs on authenticated routes that are not in `NO_CSRF_ENDPOINTS`; the public-routes path bypasses it. **Issue 2 -> The confirmation token is exposed to anyone who views the GET page.** ```ts // packages/server/src/api/controllers/ai/chatIdentityLinks.ts:40-60 const renderLinkConfirmationPage = (session, action) => { ... return ` ... Confirm ...` } ``` The `confirmationToken` (a `newid()` UUIDv4 generated by `prepareChatIde

Affected packages

EcosystemPackageAffected versionsFixed versions
npm@budibase/server

Remediation: No patched version is listed by GitHub.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.