CVE-2026-48782: pydantic-ai: SSRF blocklist bypass via IPv4-compatible, SIIT/IVI, and local NAT64 IPv6 addresses (incomplete fix of CVE-2026-46678)
Pydantic AI's cloud-metadata blocklist can be bypassed with IPv6 transition addresses it did not decode: IPv4-compatible, NAT64 local-use and operator-chosen prefixes, and ISATAP. It matters only when an app sets `force_download='allow-local'` on a URL that untrusted input can influence, on a network that routes those forms. Fixed in pydantic-ai and pydantic-ai-slim 1.102.0 and 2.0.0b3.
Published Jun 26, 2026 · Analysis Oct 25, 2026
What an attacker can do
Since 1.56.0 Pydantic AI blocks downloads of FileUrl types (ImageUrl, AudioUrl, VideoUrl, DocumentUrl) that point at private or cloud-metadata IPs. Setting force_download='allow-local' turns off the private-IP block, but cloud metadata endpoints were meant to stay blocked. The earlier fix for CVE-2026-46678 decoded IPv4-mapped IPv6, 6to4 and the NAT64 well-known prefix only. An attacker could encode the metadata IP as ::a.b.c.d, in the 64:ff9b:1::/48 prefix, in another NAT64 prefix, or as ISATAP. The wrapped address then reaches the IPv4 metadata endpoint, and the response can expose short-term cloud IAM credentials.
The bar is high, and the advisory says so (CVSS 6.8, medium, attack complexity high). The app must opt into allow-local on an attacker-influenced URL. The network must also route the form: NAT64 for the NAT64 variants, including some IPv6-only or dual-stack Kubernetes setups, or an ISATAP tunnel. A standard dual-stack cloud VM or container does not route them. The IPv4-compatible and Teredo forms are deprecated and fixed as defense in depth.
Who is affected
pydantic-ai and pydantic-ai-slim from 1.56.0 up to but not including 1.102.0, and 2.0.0b1 up to but not including 2.0.0b3. Not affected: apps that download only from developer-controlled URLs, and the bundled Agent.to_web / clai web, VercelAIAdapter and AGUIAdapter / Agent.to_ag_ui, which do not take force_download from external data.
pip show pydantic-ai-slim pydantic-aiThen search your code for force_download='allow-local'.
pydantic-ai-slim
- Ecosystem
- PyPI
- Affected
- >= 1.56.0, < 1.102.0
- Fixed in
- 1.102.0
- Check
pip show pydantic-ai-slim
pydantic-ai
- Ecosystem
- PyPI
- Affected
- >= 1.56.0, < 1.102.0
- Fixed in
- 1.102.0
- Check
pip show pydantic-ai
pydantic-ai
- Ecosystem
- PyPI
- Affected
- >= 2.0.0b1, < 2.0.0b3
- Fixed in
- 2.0.0b3
- Check
pip show pydantic-ai
pydantic-ai-slim
- Ecosystem
- PyPI
- Affected
- >= 2.0.0b1, < 2.0.0b3
- Fixed in
- 2.0.0b3
- Check
pip show pydantic-ai-slim
How to fix it
Upgrade to 1.102.0, or to 2.0.0b3 on the 2.0 pre-release line.
pip install --upgrade "pydantic-ai-slim>=1.102.0"The blocklists now decode the embedded IPv4 in every standard IPv6 transition form before checking it: IPv4-mapped, IPv4-compatible, 6to4, NAT64 at all prefix lengths, ISATAP and Teredo. The always-blocked list of cloud metadata endpoints also grew across providers (pull request 5596).
Workaround on older versions: do not use force_download='allow-local' on URLs untrusted input can influence. If you must, resolve the hostname yourself and check the result against your own metadata blocklist, transition forms included, before you build the FileUrl.
How to tell if you were hit
Search code for force_download='allow-local', then trace whether the URL value comes from users, documents or tool output. In egress and DNS logs, look for requests from the agent host to IPv6 addresses that embed a metadata IP, such as ::169.254.169.254 or addresses in 64:ff9b:1::/48. Check cloud audit logs for use of the instance role's temporary credentials from unfamiliar addresses.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
When an application using Pydantic AI opts a URL into force_download='allow-local' (which disables the default block on private/internal IPs) and runs on a network that routes the affected IPv6 transition forms (NAT64- or ISATAP-configured networks), the cloud-metadata blocklist could be bypassed by encoding the metadata IP in an IPv6 transition form that the previous fix did not decode — IPv4-compatible IPv6 (::a.b.c.d), the NAT64 RFC 8215 local-use prefix (64:ff9b:1::/48), operator-chosen NAT64 prefixes, or ISATAP. The IPv6 wrapper is then delivered to the underlying IPv4 metadata endpoint, exposing cloud IAM short-term credentials.
The bypass is exploitable only in environments whose network actually routes these forms — NAT64-configured networks (IPv6-only or dual-stack-with-NAT64 deployments, including some Kubernetes setups) for the NAT64 variants, or networks with an ISATAP tunnel for ISATAP. A standard dual-stack cloud VM or container does not route them and is not affected in practice. The IPv4-compatible and Teredo variants are deprecated and addressed as defense-in-depth.
This is an incomplete fix of GHSA-cqp8-fcvh-x7r3 / CVE-2026-46678 (itself a follow-up to CVE-2026-25580). The prior remediation decoded only IPv4-mapped IPv6, 6to4, and the NAT64 well-known prefix; the metadata guarantee did not hold for the remaining transition forms.
Severity
MEDIUM — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N = 6.8
Same impact metrics and narrow attack surface as the parent advisory (AC:H): exploitation requires the application to have opted into allow-local on a URL influenced by untrusted input, and the NAT64/ISATAP variants additionally require the deployment network to route those forms.
CWE-918: Server-Side Request Forgery (SSRF)
Affected Versions
| Package | Vulnerable | Patched | | `pydantic-ai` | `>= 1.56.0, < 1.102.0`; `>= 2.0.0b1, < 2.0.0b3` | `1.102.0`; `2.0.0b3` | | `pydantic-ai-slim` | `>= 1.56.0, < 1.102.0`; `>= 2.0.0b1, < 2.0.0b3` | `1.102.0`; `2.0.0b3` |
These transition forms have not been decoded since SSRF protection was introduced in 1.56.0.
Who Is Affected
Users are affected only if their application explicitly opts a FileUrl (ImageUrl, AudioUrl, VideoUrl, DocumentUrl) into force_download='allow-local' on a URL that is, or could be, influenced by untrusted input.
Beyond that precondition, the affected encodings only reach a metadata endpoint in environments whose network actually routes them. The broadly-routable IPv4-mapped form was addressed in 1.99.0 (CVE-2026-46678); the additional forms addressed here require a NAT64-configured network (IPv6-only or dual-stack-with-NAT64 deployments, including some Kubernetes setups) for the NAT64 variants, or an ISATAP tunnel for the ISATAP variant. The IPv4-compatible and Teredo forms are deprecated and not routed by modern stacks; they are addressed as defense-in-depth. Most deployments on a standard dual-stack cloud VM or container are therefore not exploitable in practice, but the fix restores the "always blocked" guarantee for the environments that are.
Users are not affected if they use any of the bundled integrations to ingest user input, because they do not propagate force_download from external data:
Agent.to_web/clai webVercelAIAdapterAGUIAdapter/Agent.to_ag_ui
Applications that only download from developer-controlled URLs are not affected.
Remediation
Upgrade to 1.102.0 or later (or 2.0.0b3 or later on the 2.0 pre-release line). The cloud-metadata and private-IP blocklists now decode the embedded IPv4 of every standardized IPv6 transition form before evaluating it — IPv4-mapped, IPv4-compatible, 6to4, NAT64 across all prefix lengths (including the RFC 8215 local-use prefix and operator-chosen prefixes), ISATAP, and Teredo. The set of always-blocked cloud metadata/credential endpoints has also been expanded across providers.
Workaround for Unpatched Versions
Avoid passing force_download='allow-local' on any URL that could be influenced by untrusted input. If developers must, resolve the hostname themselves and validate the result against their own metadata blocklist — including IPv6 transition forms — before constructing the FileUrl.
Credits
Reported by @SnailSploit.
Credited to SnailSploit, 0xShemesh.
Questions
- Is my default Pydantic AI setup vulnerable?
Only if you pass
force_download='allow-local'. Without that opt-in the default private-IP block applies. The bypass also needs a network that routes NAT64 or ISATAP forms.- How does this relate to CVE-2026-46678?
It is an incomplete fix of that issue. The earlier patch (1.99.0) decoded IPv4-mapped IPv6, 6to4 and the NAT64 well-known prefix. This one covers the other transition forms.
- Does clai web or the Vercel AI adapter expose this?
No. The advisory says
Agent.to_web,clai web,VercelAIAdapterandAGUIAdapterdo not propagateforce_downloadfrom external data, so they are not affected.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More Pydantic AI vulnerabilities
All Pydantic AI entriesCVE-2026-107286
Pydantic AI: Concurrency-limited models can keep their slot when a streamed request ends early
high7.5Oct 8, 2026CVE-2026-107287
Pydantic AI: Excessive resource use when local web fetching converts nested HTML
medium6.5Oct 8, 2026CVE-2026-107289
Pydantic AI: SSRF cloud-metadata blocklist bypass via IPv6 zone identifiers
medium6.8Oct 8, 2026CVE-2026-107290
Pydantic AI: Event loop blocked by quadratic title extraction in `web_fetch`
medium6.5Oct 8, 2026CVE-2026-107291
Pydantic AI OpenTelemetry instrumentation: exception events on tool and agent run spans include content when `include_content=False`
low2.3Oct 8, 2026CVE-2026-107292
Pydantic AI Web chat UI (`Agent.to_web()`, `clai web`): the local chat endpoint does not validate the `Host` header
medium6.4Oct 8, 2026
