PYSEC-2026-3572

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/flyto-core/PYSEC-2026-3572.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-3572
Aliases
Published
2026-08-04T11:34:45.633302Z
Modified
2026-08-04T14:30:16.323180146Z
Severity
  • 8.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N CVSS Calculator
Summary
Flyto2 Core: Multiple HTTP-family modules fetch client-controlled URLs without the SSRF guard their siblings apply (SSRF to internal/metadata)
Details

Summary

Numerous HTTP-emitting modules (core.api.http_get, core.api.http_post, graphql.query/graphql.mutation, monitor.http_check, communication.slack_send, notification.{discord,slack,teams}.send_message, ai.vision_analyze [anthropic path], verify.visual_diff, browser.proxy_rotate, and the agent/llm inline base_url branch) perform outbound requests to a fully client-controlled URL without calling the project's own SSRF guard (validate_url_with_env_config) that their sibling modules apply. An authenticated workflow-author can point the URL at the cloud metadata IP (169.254.169.254), a loopback/RFC1918 host, or any internal host and read the response, yielding cloud-metadata credential theft and internal service read/write.

Root Cause

The SSRF guard is per-module (there is NO global egress interception). Each module must call validate_url_with_env_config before issuing a request. The listed modules never call it — they only carry an ssrf_protected metadata tag string which enforces nothing. Exemplar: src/core/modules/third_party/developer/http/requests.py — grepping for validate_url|ssrf|is_private in requests.py returns 0 guard calls; session.get(url) fires at :85 (HTTPGetModule) and session.post at :188 (HTTPPostModule). SECURITY.md incorrectly lists api.http_get as SSRF-protected.

Impact

Readable SSRF: full {status_code, headers, body} returned to the caller (requests.py:96-108). Enables theft of cloud IAM credentials from the metadata endpoint and read/write access to internal-only APIs. Scope Changed (S:C) — the request crosses into cloud-metadata / internal-network authority the workflow layer does not otherwise have.

Proof of Concept

Verified live this session: core.api.http_get with url pointed at a loopback internal server returned the internal body INTERNAL-SECRET-IAM-CREDENTIALS, while the guarded sibling http.get returned NETWORK_ERROR: Hostname blocked: 127.0.0.1 on the same input — proving the branch-asymmetry is real (not a port artifact).

POST /mcp {"method":"tools/call","params":{"name":"execute_module",
  "arguments":{"module_id":"core.api.http_get",
  "params":{"url":"http://<cloud-metadata-ip>/latest/meta-data/iam/security-credentials/"}}}}

Attack Chain

  1. Entry: authenticated MCP client → POST /mcp execute_module core.api.http_get, url set to the cloud metadata endpoint. Guard: require_auth (mcp.py:71). Bypass proof: passes with a valid workflow-author bearer token (PR:L).
  2. Check: capability denylist / enforce_module_policy (base.py:240). Bypass proof: core.api.* not in _DEFAULT_DENYLIST (module_policy.py:45-67) → is_allowed=True (runtime-registration verified: core.api.http_get -> HTTPGetModule).
  3. Check: SSRF validation. Bypass proof: requests.py has ZERO validate_url/ssrf calls — only the ssrf_protected tag string at :23/:116. session.get(url) fires at :85.
  4. Sink: aiohttp GET/POST to the cloud metadata IP.
  5. Impact: full response body returned → IAM credential theft, internal read/write.

Bypass Evidence

Grepping validate_url|ssrf|is_private in requests.py → 0 guard calls (2 hits are both the inert tag string). Live PoC returned internal body directly; guarded sibling blocked the same input. Direct IP works — no IPv6 transition trick needed (AC:L), unlike the seed CVE-2026-55787.

Affected Versions

<= 2.26.6 — code present on latest release tag v2.26.6 (requests.py:19,112).

Suggested Fix

Call validate_url_with_env_config(url) in each listed module before issuing the outbound request, matching the guarded siblings (e.g. ai.model:157, http.get). Best: route all outbound HTTP through a single guarded client wrapper so new modules inherit the guard.

Credit

Vulnerability discovered by zx (Jace).

References

Affected packages

PyPI / flyto-core

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
2.26.7

Affected versions

1.*
1.0.0
1.0.1
1.0.2
1.0.3
1.0.4
1.0.5
1.0.6
1.0.7
1.0.8
1.0.9
1.1.0
1.1.1
1.2.0
1.3.0
1.4.0
1.5.0
1.5.1
1.5.2
1.5.4
1.6.0
1.6.1
1.6.2
1.6.3
1.6.4
1.6.5
1.7.0
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.7.6
1.7.7
1.7.8
1.7.9
1.8.0
1.8.1
1.8.2
1.8.3
1.8.4
1.8.5
1.8.6
1.8.7
1.8.8
1.8.9
1.8.10
1.8.11
1.8.12
1.8.13
1.8.14
1.8.15
1.8.16
1.8.17
1.9.0
1.11.0
1.12.0
1.13.0
1.14.0
1.14.1
1.14.2
1.15.0
1.16.0
1.16.1
1.16.2
1.16.3
1.16.4
1.16.5
1.16.6
1.16.7
1.16.8
1.16.9
1.16.10
2.*
2.0.0
2.0.1
2.0.2
2.0.3
2.0.4
2.0.5
2.1.0
2.1.1
2.1.2
2.1.3
2.1.4
2.2.0
2.2.1
2.2.2
2.3.0
2.3.1
2.4.0
2.4.1
2.4.2
2.4.3
2.4.4
2.4.5
2.4.6
2.4.7
2.5.0
2.5.1
2.5.2
2.6.0
2.6.1
2.7.0
2.7.1
2.7.2
2.7.3
2.7.4
2.7.5
2.7.6
2.8.0
2.9.0
2.10.0
2.11.0
2.12.0
2.12.1
2.12.2
2.12.3
2.12.4
2.12.5
2.12.6
2.12.13
2.12.15
2.12.16
2.12.17
2.12.18
2.12.19
2.12.20
2.12.21
2.12.22
2.12.23
2.12.24
2.12.25
2.12.26
2.12.27
2.12.28
2.13.0
2.13.1
2.13.2
2.13.3
2.13.4
2.14.0
2.15.0
2.15.1
2.15.2
2.15.3
2.16.1
2.16.3
2.16.4
2.17.0
2.17.1
2.17.2
2.17.3
2.17.4
2.17.5
2.17.6
2.17.7
2.17.8
2.18.0
2.18.1
2.18.2
2.18.3
2.18.4
2.18.5
2.18.6
2.18.8
2.18.9
2.18.10
2.18.11
2.19.0
2.20.0
2.20.1
2.20.2
2.20.3
2.20.4
2.23.0
2.23.1
2.23.2
2.23.3
2.24.0
2.24.1
2.24.2
2.24.3
2.24.4
2.25.0
2.25.1
2.25.2
2.25.3
2.25.4
2.25.5
2.25.6
2.25.7
2.25.8
2.25.9
2.25.10
2.25.11
2.25.12
2.25.13
2.25.14
2.25.15
2.25.16
2.25.17
2.25.18
2.25.19
2.25.20
2.25.21
2.25.22
2.25.23
2.25.24
2.25.25
2.25.26
2.25.27
2.26.0
2.26.1
2.26.2
2.26.3
2.26.4
2.26.5

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/flyto-core/PYSEC-2026-3572.yaml"