Description
Summary
@ai-sdk/provider-utils@4.0.19 introduced SSRF protection (validateDownloadUrl) in PR #13085 (commit ad4cfc2) that enforces an http:/https:-only protocol allowlist on all URLs passed through downloadBlob() and download(). This rejects data: URIs, which are a legitimate and commonly-used way to pass inline images to the SDK.
Reproduction
import { generateText } from "ai";
const image = "..."; // base64-encoded PNG
await generateText({
model: yourModel,
messages: [
{
role: "user",
content: [
{
type: "image",
image: `data:image/png;base64,${image}`,
},
{ type: "text", text: "Describe this image" },
],
},
],
});
This throws:
DownloadError: URL scheme must be http or https, got data:
Expected behavior
data: URIs should be accepted as valid inline image sources. They are not fetched over the network and pose no SSRF risk — the data is already embedded in the URI itself.
Root cause
validateDownloadUrl() in packages/provider-utils/src/validate-download-url.ts uses a strict protocol allowlist (lines 22-27):
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new DownloadError({
url,
message: `URL scheme must be http or https, got ${parsed.protocol}`,
});
}
This is applied unconditionally in downloadBlob() and download() before any protocol-specific handling. The data: protocol is safe — it encodes content inline and makes no network request — but is caught by the blanket rejection.
Suggested fix
The validation should either:
- Allowlist
data: alongside http:/https: — data: URIs are parsed in-process by fetch() and never leave the machine, so they carry no SSRF risk.
- Skip validation entirely for
data: URIs — check for data: before calling validateDownloadUrl() and handle them through a non-network code path.
- Make the allowlist configurable — the PR's own "Future Work" section acknowledges this: "Consider making the blocklist configurable for users who need to access private networks intentionally."
Option 1 is the simplest fix. The change in validate-download-url.ts would be:
if (
parsed.protocol !== "http:" &&
parsed.protocol !== "https:" &&
parsed.protocol !== "data:"
) {
throw new DownloadError({
url,
message: `URL scheme must be http or https, got ${parsed.protocol}`,
});
}
AI SDK Version
@ai-sdk/provider-utils: 4.0.19 (broken) — works in 4.0.18
AI SDK Version
@ai-sdk/provider-utils: 4.0.19
Code of Conduct
Description
Summary
@ai-sdk/provider-utils@4.0.19introduced SSRF protection (validateDownloadUrl) in PR #13085 (commitad4cfc2) that enforces anhttp:/https:-only protocol allowlist on all URLs passed throughdownloadBlob()anddownload(). This rejectsdata:URIs, which are a legitimate and commonly-used way to pass inline images to the SDK.Reproduction
This throws:
Expected behavior
data:URIs should be accepted as valid inline image sources. They are not fetched over the network and pose no SSRF risk — the data is already embedded in the URI itself.Root cause
validateDownloadUrl()inpackages/provider-utils/src/validate-download-url.tsuses a strict protocol allowlist (lines 22-27):This is applied unconditionally in
downloadBlob()anddownload()before any protocol-specific handling. Thedata:protocol is safe — it encodes content inline and makes no network request — but is caught by the blanket rejection.Suggested fix
The validation should either:
data:alongsidehttp:/https:—data:URIs are parsed in-process byfetch()and never leave the machine, so they carry no SSRF risk.data:URIs — check fordata:before callingvalidateDownloadUrl()and handle them through a non-network code path.Option 1 is the simplest fix. The change in
validate-download-url.tswould be:AI SDK Version
@ai-sdk/provider-utils: 4.0.19 (broken) — works in 4.0.18AI SDK Version
@ai-sdk/provider-utils: 4.0.19Code of Conduct