PYSEC-2026-2981 
## 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](https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-cqp8-fcvh-x7r3) / [CVE-2026-46678](https://nvd.nist.gov/vuln/detail/CVE-2026-46678) (itself a follow-up to [CVE-2026-25580](https://nvd.nist.gov/vuln/detail/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 web`
- `VercelAIAdapter`
- `AGUIAdapter` / `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](https://snailsploit.com).
CVSS v3.1 score: 6.8