Skip to content

fix(security): add URL validation to prevent SSRF in download functions#13085

Merged
gr2m merged 1 commit intomainfrom
fix/ssrf-download-validation
Mar 5, 2026
Merged

fix(security): add URL validation to prevent SSRF in download functions#13085
gr2m merged 1 commit intomainfrom
fix/ssrf-download-validation

Conversation

@gr2m
Copy link
Collaborator

@gr2m gr2m commented Mar 5, 2026

Background

The downloadBlob function (@ai-sdk/provider-utils) and download function (ai) fetch user-provided URLs without any validation. When users pass image URLs to generateImage(), strings starting with "http" flow directly to fetch() via downloadBlob(file.url) in OpenAI, OpenAI-compatible, and DeepInfra image models. This enables blind SSRF — attackers can make the server request internal resources (localhost, cloud metadata endpoints like 169.254.169.254, private IPs).

Summary

Added a shared validateDownloadUrl utility in @ai-sdk/provider-utils that both downloadBlob and download call before fetch. It throws DownloadError if the URL is unsafe:

  • Protocol — only http: and https: allowed
  • Hostname — blocks localhost, *.local, *.localhost, empty hostname
  • IPv4 — blocks private ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 0.0.0.0/8
  • IPv6 — blocks ::1, fc00::/7, fe80::/10, ::, and IPv4-mapped addresses (::ffff:x.x.x.x)

No DNS resolution checks (not available in edge runtimes). Reuses DownloadError for rejected URLs.

Manual Verification

  • Ran pnpm vitest run src/validate-download-url.test.ts in packages/provider-utils — 29 tests pass
  • Ran pnpm vitest run src/download-blob.test.ts in packages/provider-utils — 13 tests pass
  • Ran pnpm vitest run src/util/download/download.test.ts in packages/ai — 7 tests pass

Checklist

  • Tests have been added / updated (for bug fixes / features)
  • Documentation has been added / updated (for bug fixes / features)
  • A patch changeset for relevant packages has been added (for bug fixes / features - run pnpm changeset in the project root)
  • I have reviewed this pull request (self-review)

Future Work

  • DNS rebinding protection could be added if a DNS resolution API becomes available in edge runtimes
  • Consider making the blocklist configurable for users who need to access private networks intentionally

Add validateDownloadUrl utility that blocks private/internal addresses,
non-HTTP protocols, and localhost before fetching user-provided URLs
in downloadBlob and download functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tigent tigent bot added ai/provider related to a provider package. Must be assigned together with at least one `provider/*` label bug Something isn't working as documented labels Mar 5, 2026
@gr2m gr2m added the backport Admins only: add this label to a pull request in order to backport it to the prior version label Mar 5, 2026
@gr2m gr2m marked this pull request as ready for review March 5, 2026 00:57
@gr2m gr2m merged commit ad4cfc2 into main Mar 5, 2026
29 checks passed
@gr2m gr2m deleted the fix/ssrf-download-validation branch March 5, 2026 00:57
vercel-ai-sdk bot pushed a commit that referenced this pull request Mar 5, 2026
@vercel-ai-sdk vercel-ai-sdk bot removed the backport Admins only: add this label to a pull request in order to backport it to the prior version label Mar 5, 2026
@vercel-ai-sdk
Copy link
Contributor

vercel-ai-sdk bot commented Mar 5, 2026

⚠️ Backport to release-v5.0 created but has conflicts: #13086

@vercel-ai-sdk
Copy link
Contributor

vercel-ai-sdk bot commented Mar 5, 2026

🚀 Published in:

Package Version
ai 6.0.116
@ai-sdk/alibaba 1.0.10
@ai-sdk/amazon-bedrock 4.0.77
@ai-sdk/angular 2.0.117
@ai-sdk/anthropic 3.0.58
@ai-sdk/assemblyai 2.0.24
@ai-sdk/azure 3.0.42
@ai-sdk/baseten 1.0.38
@ai-sdk/black-forest-labs 1.0.24
@ai-sdk/bytedance 1.0.4
@ai-sdk/cerebras 2.0.39
@ai-sdk/cohere 3.0.25
@ai-sdk/deepgram 2.0.24
@ai-sdk/deepinfra 2.0.39
@ai-sdk/deepseek 2.0.24
@ai-sdk/elevenlabs 2.0.24
@ai-sdk/fal 2.0.25
@ai-sdk/fireworks 2.0.40
@ai-sdk/gateway 3.0.66
@ai-sdk/gladia 2.0.24
@ai-sdk/google 3.0.42
@ai-sdk/google-vertex 4.0.79
@ai-sdk/groq 3.0.29
@ai-sdk/huggingface 1.0.37
@ai-sdk/hume 2.0.24
@ai-sdk/klingai 3.0.8
@ai-sdk/langchain 2.0.122
@ai-sdk/llamaindex 2.0.116
@ai-sdk/lmnt 2.0.24
@ai-sdk/luma 2.0.24
@ai-sdk/mcp 1.0.25
@ai-sdk/mistral 3.0.24
@ai-sdk/moonshotai 2.0.10
@ai-sdk/open-responses 1.0.6
@ai-sdk/openai 3.0.41
@ai-sdk/openai-compatible 2.0.35
@ai-sdk/perplexity 3.0.23
@ai-sdk/prodia 1.0.21
@ai-sdk/provider-utils 4.0.19
@ai-sdk/react 3.0.118
@ai-sdk/replicate 2.0.24
@ai-sdk/revai 2.0.24
@ai-sdk/rsc 2.0.116
@ai-sdk/svelte 4.0.116
@ai-sdk/togetherai 2.0.39
@ai-sdk/valibot 2.0.20
@ai-sdk/vercel 2.0.37
@ai-sdk/vue 3.0.116
@ai-sdk/xai 3.0.67

gr2m added a commit that referenced this pull request Mar 5, 2026
…ad functions (#13086)

This is an automated backport of #13085 to the release-v5.0 branch.

---------

Co-authored-by: Gregor Martynus <39992+gr2m@users.noreply.github.com>
gr2m added a commit that referenced this pull request Mar 5, 2026
…vent SSRF bypass (#13111)

## Background

The existing `validateDownloadUrl` (added in #13085) only validates the
initial URL before `fetch()`. Since `fetch()` follows HTTP redirects by
default, an attacker can bypass SSRF protections by providing a
safe-looking public URL that 302-redirects to internal endpoints (e.g.,
`http://169.254.169.254/latest/meta-data/`), enabling in-band response
body exfiltration through the AI model's response.

## Summary

Added `response.redirected` check with
`validateDownloadUrl(response.url)` in both `downloadBlob`
(`@ai-sdk/provider-utils`) and `download` (`ai`) functions to validate
the final URL after following redirects, before reading the response
body.

## Manual Verification

- `pnpm vitest run packages/provider-utils/src/download-blob.test.ts` —
16 tests pass
- `pnpm vitest run
packages/provider-utils/src/validate-download-url.test.ts` — 29 tests
pass
- `pnpm vitest run packages/ai/src/util/download/download.test.ts -t
"SSRF"` — 5 tests pass
vercel-ai-sdk bot pushed a commit that referenced this pull request Mar 5, 2026
…vent SSRF bypass (#13111)

## Background

The existing `validateDownloadUrl` (added in #13085) only validates the
initial URL before `fetch()`. Since `fetch()` follows HTTP redirects by
default, an attacker can bypass SSRF protections by providing a
safe-looking public URL that 302-redirects to internal endpoints (e.g.,
`http://169.254.169.254/latest/meta-data/`), enabling in-band response
body exfiltration through the AI model's response.

## Summary

Added `response.redirected` check with
`validateDownloadUrl(response.url)` in both `downloadBlob`
(`@ai-sdk/provider-utils`) and `download` (`ai`) functions to validate
the final URL after following redirects, before reading the response
body.

## Manual Verification

- `pnpm vitest run packages/provider-utils/src/download-blob.test.ts` —
16 tests pass
- `pnpm vitest run
packages/provider-utils/src/validate-download-url.test.ts` — 29 tests
pass
- `pnpm vitest run packages/ai/src/util/download/download.test.ts -t
"SSRF"` — 5 tests pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai/provider related to a provider package. Must be assigned together with at least one `provider/*` label bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant