CVE-2026-55787
Summary
flyto-core's SSRF protection (validate_url_ssrf / is_private_ip in src/core/utils.py) blocks private and metadata destinations by resolving the host and testing the resulting IP for membership in a hardcoded PRIVATE_IP_RANGES list. That list contains only the *native* RFC 1918 / loopback / link-local / unique-local ranges. It does not account for IPv6 transition address forms that embed an IPv4 (or loopback) target:
- IPv4-mapped ::ffff:a.b.c.d
- IPv4-compatible ::a.b.c.d
- 6to4 2002::/16
- NAT64 well-known prefix 64:ff9b::/96 and local-use 64:ff9b:1::/48
A workflow author can submit a URL with a literal transition-form host (for example http://[::ffff:127.0.0.1]:8080/... or http://[64:ff9b::a9fe:a9fe]/latest/meta-data/). is_private_ip() returns False for these (the address is not literally inside any listed range), so validate_url_ssrf lets the request through, and the http.get atomic module (and ~10 sibling modules that share the same guard) performs the outbound aiohttp fetch and returns the response body. On a host that uses NAT64/6to4 these addresses route to the embedded IPv4 endpoint (e.g. the cloud instance-metadata service 169.254.169.254); on any dual-stack host the IPv4-mapped form is routed by the kernel directly to the embedded IPv4, including loopback and RFC 1918 internal services.
This is CWE-918 (Server-Side Request Forgery): the guard that exists specifically to keep workflow-authored URLs away from internal/metadata endpoints is bypassable, and the response body is returned to the caller (a read SSRF).
Affected code
src/core/utils.py:
- PRIVATE_IP_RANGES (around L297) — lists native ranges only; no 64:ff9b::/96, no 2002::/16, no ::ffff:0:0/96, no ::/96.
- is_private_ip(ip_str) (around L337) — ipaddress.ip_address(ip_str) then membership test against PRIVATE_IP_RANGES. Because the test is plain network membership (not is_global/is_private predicates), it does not unw
⚡ Watch CVE-2026-55787
Get an email if CVE-2026-55787 is added to CISA KEV, gains public exploit code, or a new advisory cites it — max one per day, one-click unsubscribe.
Advisory coverage (1)
External references
Embed the live status
— this badge updates automatically when the KEV or exploit status changes. How to embed it →
[](https://www.csirts.com/cve/CVE-2026-55787)