CVE-2026-48149
Description
Budibase is an open-source low-code platform. Prior to 3.39.0, the Budibase Text component renders markdown by assigning marked.parse(markdown) straight to innerHTML with no sanitizer (packages/bbui/src/Markdown/MarkdownViewer.svelte:22). Any column a builder binds to a Text component in Markdown mode is a stored-XSS sink writable by every BASIC app user with WRITE on the underlying table. This vulnerability is fixed in 3.39.0.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Unsanitized Markdown rendering in Budibase's Text component allows BASIC users to inject stored XSS that executes in admin sessions via a srcdoc CSP bypass.
Vulnerability
In Budibase versions prior to 3.39.0, the Text component renders Markdown by assigning the output of marked.parse(markdown) directly to innerHTML with no sanitizer, as seen in packages/bbui/src/Markdown/MarkdownViewer.svelte:22 [1]. Any column that a builder binds to a Text component in Markdown mode becomes a stored-XSS sink; this sink is writable by every BASIC app user who has WRITE permission on the underlying table [1]. The affected component is shipped in the published app via packages/client/src/components/app/Text.svelte:57, which passes the user-submitted column value straight to MarkdownViewer [1].
Exploitation
An attacker with BASIC role on the app (e.g., Bob, invited by admin Alice) needs WRITE access to a table column that is bound to a Text component in Markdown mode. The attacker submits a row whose body contains an ` element. The srcdoc document inherits the parent's Content Security Policy (CSP); Budibase's CSP includes the CDN origin d2l5prqdbvm3op.cloudfront.net in its script-src allowlist [1]. Because that CloudFront distribution serves both Budibase's own runtime and user-uploaded attachments, and the srcdoc` is same-origin with the app, the injected script executes when an admin (Alice) opens the screen that renders the malicious row [1]. The attack was verified end-to-end on Budibase Cloud and self-hosted version 3.35.8 [1].
Impact
Successful exploitation results in stored cross-site scripting (XSS) – the attacker's JavaScript code runs in the session of any user who views the compromised screen, including global admins [1]. The attacker can perform any action on the Budibase instance that the victim can, such as modifying apps, accessing data, or escalating privileges, entirely under the identity of the victim [1]. The impact is information disclosure, data manipulation, and potential full account takeover.
Mitigation
The vulnerability is fixed in Budibase version 3.39.0 [1]. Users should upgrade to this version or later. If upgrading is not immediately possible, ensure that no Text component is configured in Markdown mode for any column that BASIC users can write to; alternatively, remove WRITE permissions from untrusted roles on columns used with Markdown mode as a temporary workaround [1]. No public CISA KEV listing has been associated with this CVE as of the publication date.
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
1feab9955aeb5Bump version to 3.39.0
1 file changed · +1 −1
lerna.json+1 −1 modified@@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.38.5", + "version": "3.39.0", "npmClient": "yarn", "concurrency": 20, "command": {
Vulnerability mechanics
Root cause
"Missing output sanitization in MarkdownViewer.svelte:22 assigns marked.parse(markdown) directly to innerHTML with no sanitizer, allowing arbitrary HTML/JavaScript injection through user-submitted markdown content."
Attack vector
An attacker with BASIC role and WRITE permission on a table submits a row whose body column contains a malicious markdown payload. When a builder or admin views a screen that renders that column through a Text component in Markdown mode, the payload executes. On Budibase Cloud, the attacker uploads a .mjs file to the attachment endpoint (which serves from the same CloudFront CDN listed in script-src), then injects an <iframe srcdoc> tag referencing that .mjs file. The srcdoc document inherits the app's CSP, trusts the CDN origin, and is same-origin with the app, so the attacker's script runs in the victim's session. On self-hosted, the attacker uploads an .html file (extension denylist is skipped for non-PUBLIC users) and either sends the direct URL for one-click XSS or injects an <iframe src> into the markdown column for zero-click XSS [ref_id=1].
Affected code
The sink is at packages/bbui/src/Markdown/MarkdownViewer.svelte:22, where `ref.innerHTML = marked.parse(markdown, { async: false })` assigns unsanitized markdown output to innerHTML. The published-app Text component at packages/client/src/components/app/Text.svelte:57 binds user-submitted column values and forwards them to MarkdownViewer with no sanitizer in between [ref_id=1].
What the fix does
The patch sanitizes the markdown output before assigning to innerHTML by introducing DOMPurify.sanitize() with the HTML profile at MarkdownViewer.svelte:22 [patch_id=2725525]. This drops <iframe>, <script>, inline event handlers, and javascript: URIs, removing the sink for both the Cloud and self-hosted variants. The advisory also recommends serving attachments with Content-Disposition: attachment, moving user content to a dedicated origin not on script-src, converting the extension check from a denylist to an allowlist, and setting httpOnly: true on the session cookie [ref_id=1].
Preconditions
- authAttacker must have BASIC role (App User) on the published app
- configAttacker must have WRITE permission on a table that has a column rendered through a Text component in Markdown mode
- inputVictim (admin/builder) must view a screen that renders the attacker-controlled column through the Text component
- networkNetwork access to the Budibase application (Cloud or self-hosted)
Reproduction
**Cloud variant (verified on iwnyub5k.budibase.app):** 1. Admin creates an app with a table `notes` having a `body` (Long form text) column, grants BASIC users READ+WRITE on the table, adds a screen with Data Provider→Repeater→Text component (Format=Markdown) bound to `{{ Repeater.body }}`, publishes, and invites Bob as BASIC App User. 2. Bob uploads `xss.mjs` containing `alert("origin=" + document.domain + "\ncookie=" + document.cookie)` via `POST /api/attachments/:tableId/upload`. 3. Bob submits a note whose body is `<iframe srcdoc='<script src="https://d2l5prqdbvm3op.cloudfront.net/app_.../attachments/<uuid>.mjs?Expires=...&Signature=..."></script>'></iframe>`. 4. Alice opens the notes screen; the alert shows the admin's origin and session cookie [ref_id=1]. **Self-hosted variant (verified on 3.35.8):** 1. Same app setup. 2. Bob uploads `xss.html` containing `<script>alert("origin="+document.domain+"\ncookie="+document.cookie)</script>` via the attachment endpoint. 3. Bob sends Alice the returned URL `/files/signed/prod-budi-app-assets/<app>/attachments/<uuid>.html`. 4. Alice clicks; the browser navigates to a same-origin document with no CSP, inline script runs, alert shows the admin's origin and session cookie [ref_id=1].
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
1News mentions
0No linked articles in our index yet.