{"id":"CVE-2026-46678","aliases":["GHSA-cqp8-fcvh-x7r3","PYSEC-2026-2978","PYSEC-2026-2982"],"title":"Pydantic AI: SSRF cloud-metadata blocklist bypass via IPv4-mapped IPv6 (Incomplete fix of CVE-2026-25580)","summary":"Pydantic AI: SSRF cloud-metadata blocklist bypass via IPv4-mapped IPv6 (Incomplete fix of CVE-2026-25580)","severity":"medium","cvss":6.8,"cvssVector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N","vendor":"pydantic-ai","product":"pydantic-ai","ecosystem":"pip","affected":["pydantic-ai >= 1.56.0, < 1.99.0","pydantic-ai-slim >= 1.56.0, < 1.99.0"],"patched":["pydantic-ai 1.99.0","pydantic-ai-slim 1.99.0"],"published":"2026-05-21","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-cqp8-fcvh-x7r3","references":[{"url":"https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-2jrp-274c-jhv3"},{"url":"https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-cqp8-fcvh-x7r3"},{"url":"https://github.com/pydantic/pydantic-ai"}],"tags":["osv","pip"],"ingestedAt":"2026-07-13T18:57:56.780Z","epss":0.00419,"epssPercentile":0.35873,"slug":"CVE-2026-46678","body":"## Overview\n\n## Summary\n\nWhen an application using Pydantic AI opts a URL into `force_download='allow-local'` (which disables the default block on private/internal IPs), the cloud-metadata blocklist could be bypassed by encoding the metadata IP in an IPv6 transition form (IPv4-mapped IPv6, 6to4, or NAT64). Dual-stack and translated networks route the IPv6 wrapper to the underlying IPv4 endpoint, exposing cloud IAM short-term credentials.\n\nThis is an incomplete fix of [GHSA-2jrp-274c-jhv3](https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-2jrp-274c-jhv3) / [CVE-2026-25580](https://nvd.nist.gov/vuln/detail/CVE-2026-25580). The parent advisory's remediation guaranteed that \"cloud metadata endpoints are always blocked, even with `allow-local`.\" That guarantee did not hold for IPv6-encoded forms of the metadata IPs.\n\n## Severity\n\nSame impact metrics as the parent CVE, but materially narrower attack surface (AC:H instead of AC:L), because exploitation requires the application to have opted into `allow-local` on a URL influenced by untrusted input.\n\n## Who Is Affected\n\nApplications are affected **only if** they explicitly opt for `FileUrl` (`ImageUrl`, `AudioUrl`, `VideoUrl`, `DocumentUrl`) into `force_download='allow-local'` on a URL that is, or could be, influenced by untrusted input.\n\nApplications 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:\n\n- `Agent.to_web` / `clai web`\n- `VercelAIAdapter`\n- `AGUIAdapter` / `Agent.to_ag_ui`\n\nApplications that only download from developer-controlled URLs are not affected.\n\n## Remediation\n\nUpgrade to `1.99.0` or later. The cloud-metadata and private-IP blocklists now apply to IPv6 transition forms that route to a blocked IPv4 endpoint (IPv4-mapped IPv6, 6to4, and NAT64 well-known prefix). The blocklists have also been extended to cover additional IANA-reserved IPv4 and IPv6 special-purpose ranges.\n\n## Workaround for Unpatched Versions\n\nAvoid 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-encoded forms — before constructing the `FileUrl`.\n\n## Credits\n\nReported by [j0hndo](mailto:dohyun4466@gmail.com).\n\n## Affected packages\n\n- `pydantic-ai >= 1.56.0, < 1.99.0`\n- `pydantic-ai-slim >= 1.56.0, < 1.99.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `pydantic-ai 1.99.0`\n- `pydantic-ai-slim 1.99.0`","depth":"sunlit","depthScore":37,"depthScoreParts":{"impact":37.4,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}