The Guard Checked One Host and the Client Called Another: CrewAI CVE-2026-120630 Parser-Differential SSRF

On 2 November 2026, NVD published CVE-2026-120630, a server-side request forgery flaw in CrewAI (CWE-918): the framework’s SSRF guard and its HTTP client validate different things from the same URL, so a request carrying a malicious URL — through ScrapeWebsiteTool — can reach internal hosts the guard believed it had refused. Affected versions are before 1.15.17; crewai-tools 1.15.17 carries the fix. The CVE record is still Deferred (NVD has not scored it yet); OffSeq’s analysis rates it High and records no evidence of exploitation — not in KEV, no public exploit indexed, no sensor traffic. The mechanism, though, is fully public: an independent researcher disclosed the parser confusion in September, and the vendor’s own fix commit describes the second half of the hole the CVE text summarizes.

One backslash, two parsers, two destinations

The researcher’s finding, published 5 September 2026, is a textbook parser differential. CrewAI’s guard extracts the hostname with Python’s urlparse and checks it against a blocklist; the fetch then hands the same string to requests, which parses it with urllib3 — a different parser with different opinions about edge cases. For a URL like http://127.0.0.1:8080\@1.1.1.1/, urlparse treats the backslash as an ordinary character, reads 127.0.0.1:8080\ as userinfo, and reports the host as 1.1.1.1 — public, allowed. urllib3 instead connects to 127.0.0.1:8080. The guard approves a public IP while the client calls localhost, and the researcher confirmed the disagreement across Python 3.11, 3.12 and 3.13. A /../ suffix then normalizes the mangled path away, so the internal service receives a clean request — GET /admin/secrets — with the full response body returned. The same payload worked against Langflow, which shared the urlparse-then-requests pattern; that half was assigned CVE-2026-19304, fixed in Langflow 1.11.3, while the CrewAI half waited for this week’s CVE number.

The fix validates the connection, not just the string

CrewAI’s fix, PR #6981 (merged 17 August 2026), is the interesting part for defenders because it concedes that string validation cannot win this game. The commit message is explicit: the socket’s peer can differ from the IP the validator checked, because urllib3 re-resolves DNS when it opens the connection — and behind an HTTP proxy the connected peer is the proxy, so destination checks never even fire. The replacement fetches through an SSRFProtectedAdapter that resolves the host once, rejects any private or reserved address, connects with that result, and re-checks getpeername() after connect; it also pins trust_env=False and empty proxies so ambient proxy configuration cannot silently reroute the fetch. In other words, the durable check moved from what the URL says to where the socket went — the only layer a second parser cannot reinterpret. The same advisory logic generalizes: block backslashes in URLs accepted by guards, or parse with the same parser the client uses, but connect-time peer validation is the control that survives parser drift.

Scope and what to actually do

Exposure runs through any CrewAI deployment where agent-reachable tool input can carry a URL into ScrapeWebsiteTool — which is the normal case, since web research is the tool’s job, and indirect prompt injection can supply the URL without the operator typing it. If you run crewai-tools below 1.15.17: upgrade to 1.15.17 or later (PyPI’s line has since moved to 1.15.34) and treat pre-fix deployments as SSRF-exposed toward cloud metadata endpoints and internal services. Egress filtering toward 169.254.169.254 and RFC-1918 ranges remains the backstop, because the agent use case — fetching arbitrary URLs — means the guard is the only barrier between model output and your network. This is the second CrewAI SSRF lineage we have tracked after October’s CVE-2026-112308 Selenium-tool SSRF, and the Langflow twin sits in the same remediation queue as the Langflow auto-login plus MCP RCE pair: agent frameworks whose HTTP plumbing predates their agent threat models, fixed one parser at a time.

Our verification was read-only. We read the full NVD JSON record for CVE-2026-120630 first-hand (published 2 November 2026: ScrapeWebsiteTool mechanism, pre-1.15.17 affected range, CWE-918, Deferred status, docs/PR/researcher-blog references), the researcher’s full report first-hand (5 September 2026: backslash parser differential with urlparse/urllib3 outputs, Python 3.11–3.13 confirmation, path-traversal cleanup, Langflow CVE-2026-19304 twin with 1.11.3 fix, HackerOne/Bugcrowd reporting timeline — KEV-listing remark carried as the researcher’s claim only), the full vendor fix commit first-hand via the GitHub API (PR #6981, merged 17 August 2026: peer-IP pinning, SSRFProtectedAdapter, trust_env and proxy hardening), the OffSeq threat page first-hand (High rating, fixed-in-1.15.17, no exploitation evidence on any signal), and the PyPI JSON API first-hand (1.15.17 present, line current at 1.15.34). The October CrewAI Selenium SSRF and Langflow parallels are our prior coverage linked above. We ran no CrewAI tool, crafted no SSRF URL, fetched no internal host, and contacted no one. Brave Search was used for discovery only.

Sources: