AI DevelopmentPlaybook18 min readPublished August 20, 2026

One changelog line · validate the IP you actually connect to, on every hop · an audit checklist grounded in OWASP and CWE-918

CrewAI Quietly Fixed an SSRF Gap Agent Fetch Tools Can Share

CrewAI 1.15.17, released August 20, 2026, pins SSRF checks to each redirect hop and to the resolved peer IP. It shipped as one line in the release notes, with no advisory, CVE identifier or severity attached. The gap it closes is narrower than the changelog wording suggests, and the pattern it lands on applies to every agent tool that fetches a URL a model chose.

DA
Digital Applied Team
Senior strategists · Published Aug 20, 2026
PublishedAug 20, 2026
Read time18 min
Sources11 primary
Fix shipped in
1.15.17
released Aug 20, 2026
PR #6981, merged Aug 17
Advisory with the release
None
one Bug Fixes line
no CVE, no severity
Merged SSRF PRs, May to Aug 2026
5
same fetch code path
#5711 through #6981
MCP reference fetch server
0
SSRF checks in its source
last edited Mar 15, 2026

CrewAI 1.15.17 shipped on August 20, 2026 with a Bug Fixes line most readers will scroll past: “Pin SSRF checks to each redirect hop and peer IP.” There is no advisory behind it, no CVE identifier and no severity rating. But the change it describes, pinning an agent's outbound fetch to the IP address that was actually validated, on every redirect hop, is the pattern every agent tool that fetches a model-supplied URL needs, and the one reference implementation we read for this post does not have.

The stakes are structural rather than dramatic. An agent's fetch tool takes a URL the model chose, often from content the model read elsewhere, and makes a real network request from inside your infrastructure. That is the textbook shape of server-side request forgery, CWE-918. What makes the CrewAI change worth a close read is that it is not the first SSRF hardening pass on this code path; it is the fifth since May, and the earlier June pass already earned a CVE. The August fix closes a narrower, more subtle gap than “only the first URL was checked,” and getting that distinction right is the point of this post.

This guide covers what 1.15.17 actually changed, the precise mechanism of the gap it closes, the three validation designs CrewAI has moved through on one fetch path, why one-time URL validation fails by construction according to OWASP and MITRE, what the MCP reference fetch server does by comparison, and a six-row audit checklist you can run against any fetch tool in your own harness.

Key takeaways
  1. 01
    The fix is real, and it shipped as a changelog line.CrewAI 1.15.17 (August 20) carries PR #6981, merged August 17, which pins SSRF checks to each redirect hop and to the resolved peer IP. No advisory, CVE or severity accompanied the release.
  2. 02
    The gap is narrower than “only the first URL was checked.”By mid-August CrewAI's safe_get already re-validated every redirect's Location header. What remained was a time-of-check/time-of-use mismatch: the IP validated could differ from the IP the socket connected to, plus an HTTP-proxy substitution path.
  3. 03
    This is the fifth SSRF hardening pass on one code path since May.PRs #5711, #6038, #6331, #6795 and #6981 each tightened the same fetch guard. The June pass (PR #6331) fixed CVE-2026-62240, scored 8.3 under CVSS 4.0. The August pass has no CVE at all.
  4. 04
    One-time URL validation fails by design.CWE-918 and the OWASP SSRF prevention cheat sheet both require the destination to be checked at request time. Redirects and DNS re-resolution both move the destination after the check has passed.
  5. 05
    The MCP reference fetch server has zero SSRF checks.Checked directly: it follows redirects via httpx with follow_redirects=True and contains no private-IP check, no per-hop re-validation and no peer pinning. It is a reference implementation rather than a hardened one, and it is the only fetch tool outside CrewAI we read for this post.

01What ShippedOne release, one line, no advisory.

The CrewAI 1.15.17 release was published on August 20, 2026 at 00:27 UTC. Its Bug Fixes section contains the line that prompted this post, verbatim: “Pin SSRF checks to each redirect hop and peer IP.” The commit behind it is titled “fix(tools): pin SSRF checks to each redirect hop and peer IP (#6981)”, which ties the release line to one specific pull request: PR #6981, opened and merged on August 17 by the GitHub user theCyberTech. Just under three days separate the merge from the release.

The diff is not small: 767 additions and 70 deletions across 19 files. It touches the shared safe-request helper in crewai_tools/security/safe_requests.py, adds a new security/ssrf_adapter.py, updates security/safe_path.py, the docs-site RAG loader and the URL read tool, plus five documentation files and a set of tests. One attribution note: the PR body is wrapped in markers showing it was drafted by a Cursor background agent on the contributor's behalf. The quotes in this post are therefore attributed to the PR description, not to a named engineer, and this is a routine contributor PR merged by a maintainer, not a CrewAI security bulletin.

The release carries four other items, each one line in the notes: declarative conversational-flows documentation; handling of oversized single messages during chunking; a fix to how the URL hostname is used as the server_name for MCP HTTP and SSE servers; and a fix for native tool calls that were broken over the OpenAI Responses API. None of them relate to the SSRF change.

Release-day status
The 1.15.17 release notes carry no advisory, no CVE identifier and no severity for the redirect-hop and peer-IP change, and no GitHub Security Advisory accompanied the release. The fix shipped as a single changelog line, not an advisory. That is not unusual for open-source hardening work, but it means nobody's vulnerability scanner will tell them to upgrade. A separate pull request with a near-identical title, #7001 (“pin SSRF fetches inside the outbound-fetch module”), was still open when 1.15.17 shipped; it proposes a further restructuring and is not part of this release.

02The Gap, PreciselyTime of check, time of connect.

It would be easy to read the changelog line as “CrewAI was only validating the first URL and ignoring redirects.” That was true of CrewAI's original design, and Section 03 covers it, but it was not true of the code immediately before 1.15.17. The PR description is explicit that safe_get already checked the request URL and each redirect's Location header. The problem was what happened after the check passed.

In the PR's own words, each hop still used requests.get. urllib3 resolves DNS again when it opens the socket. The IP that validate_url checks can differ from the IP that the socket uses. Validation looked at one answer; the connection used another. That is a time-of-check to time-of-use mismatch, and the attack class it admits is DNS rebinding: a hostname that resolves to a harmless public address when it is checked and to an internal one when it is dialled. The second vector is simpler. If an HTTP proxy is in the path, the proxy becomes the connected peer, and the real destination IP is never checked at all.

Vector 1
DNS re-resolution TOCTOU
validate → resolve again → connect

The guard resolved the hostname and approved the address, then handed a URL string to requests, whose transport resolved the name a second time on socket open. Two lookups, two possible answers. A rebinding hostname lands on an internal address on the second answer.

Closed by pinning the connect to the validated address
Vector 2
HTTP proxy substitution
proxy becomes the connected peer

With a proxy configured, the socket connects to the proxy, not the destination. Any check applied to the connected peer sees the proxy's address, and the destination behind it is never validated.

Closed by trust_env=False and an empty proxies map
"An HTTP proxy becomes the connected peer. Then the destination IP is not checked."— PR #6981 description, crewAIInc/crewAI, merged August 17, 2026

The fix, as the PR describes it, is a six-step sequence and a session-level change. Resolve the host once with socket.getaddrinfo. Raise an error if any returned address is private or reserved. Do not connect if that check fails. Connect with socket.connect using the address from the first step, not the hostname. After connecting, call getpeername() and re-check the peer against the same private/reserved blocklist. Close the socket if that second check fails. Alongside it, the safe-fetch session now sets trust_env=False and proxies={}, and rejects a non-empty proxies argument unless an explicit escape hatch is enabled. That session change is what closes the proxy vector; the post-connect getpeername() check is what closes the rebinding one.

Notice what is being validated now. Not a URL string, and not even a DNS answer, but the address the operating system reports as the other end of an open socket. That is the only point at which “where is this request going” has a definite answer, which is why OWASP's guidance, covered in Section 04, insists on validation at request time rather than at parse time.

PR #6981 diff
lines added, 70 removed
767

Nineteen files: the safe-request helper, a new SSRF adapter, the safe-path helper, the docs-site loader, the URL read tool, five docs files and tests.

19 files
Bypass vectors closed
rebinding and proxy substitution
2

The post-connect peer check closes the DNS re-resolution gap; the session-level proxy lockdown closes the proxy gap. Both are described at mechanism level in the PR, not as exploits.

getpeername() + trust_env=False
Merge to release
August 17 to August 20
2days 17 hrs

Merged at 07:38 UTC on August 17; published in 1.15.17 at 00:27 UTC on August 20. Fast for a framework of this size, and entirely unannounced.

no advisory in between

03Three Designs, One Code PathFrom validate-once to a pinned peer.

CrewAI's fetch guard has been through three distinct designs in 2026, and the public record is unusually complete because each step left a pull request, an issue, or a CVE behind. The first design is documented in issue #6520, filed July 12 by a security researcher and closed July 14 because it was already fixed. The issue describes the original validate_url as resolving and blocklisting the supplied hostname once, then returning the original URL string unchanged, after which the scrape tools fetched it with requests.get and default redirect-following, with no re-validation of the redirect target. A public host that responded with a 30x to an internal address walked straight past the guard. That is the design the phrase “only the initial request was checked” accurately describes.

That original bug has a CVE. According to the researcher's own timeline inside the issue, it was reported through Bugcrowd on June 12, fixed by PR #6331 (merged June 26, shipped as 1.15.1), and the Bugcrowd ticket was closed as a false positive on July 10, after the fix had already shipped. The record was published as CVE-2026-62240 on July 13, affecting CrewAI versions before 1.15.1, scored 8.3 (High) under CVSS 4.0 and 7.4 (High) under CVSS 3.1, with a weakness class of CWE-918. VulnCheck's advisory independently reports the same 8.3. Those scores belong to the June bug and to nothing else.

The second design is the one PR #6981 found in place in August: validate the request URL, and also validate each redirect's Location header. That closes the redirect bypass at the URL level and is what CVE-2026-62240 was about. It does not close the gap between the address that was validated and the address the socket dials, which is the third design's job.

The three SSRF validation designs CrewAI's fetch path has moved through in 2026: what each checks, what it does not check, the bypass class it still allows, and which CrewAI version or pull request represents it, sourced to issue 6520 and pull request 6981.
DesignWhat is checkedWhat is not checkedBypass class still openCrewAI status
Design A · validate once, then follow
Original guard (per issue #6520)The supplied hostname, resolved and blocklisted once before the fetch.Any redirect target. The URL string is returned unchanged and fetched with default redirect-following.Redirect to an internal address; DNS rebinding.Fixed by PR #6331, June 26 · 1.15.1 · CVE-2026-62240
Design B · validate the URL and each Location header
Mid-2026 safe_get (per PR #6981's description)The request URL and every redirect's Location header, each resolved and checked before the hop is followed.The address the socket actually connects to. Each hop still used requests.get, and urllib3 resolved DNS again on socket open.DNS re-resolution between check and connect (TOCTOU); HTTP proxy substituting the connected peer.State before 1.15.17 · no CVE for this gap
Design C · validate and pin the resolved peer, per hop
CrewAI 1.15.17 (PR #6981)Resolve once, reject private/reserved answers, connect to the validated address, re-check getpeername() after connecting, close on failure. Proxies disabled by default.Nothing in the redirect or resolution path that the PR describes. Application-level policy (allowlists, metadata denylists beyond private/reserved) is still your job.None of the three classes above, as described by the PR. The explicit proxies escape hatch reopens the proxy path if you enable it.Released Aug 20 · no advisory, no CVE

Zoom out and the pattern is incremental hardening rather than a single clean fix. Five merged pull requests with SSRF in the title touched this path between May 5 and August 17: #5711 on May 5 (“validate IPs on every redirect hop to prevent SSRF bypass”), #6038 on June 4 (“re-validate redirects and pin peer IP to close SSRF bypass”), #6331 on June 26 (the CVE fix), #6795 on August 3 (which closed an SSRF in the arXiv paper tool's PDF download and a DNS-rebinding gap in safe_get), and #6981 on August 17. Read the titles of #6038 and #6981 side by side and you can see that “pin the peer IP” was attempted in June and had to be revisited in August. That is what real hardening looks like on a shared fetch helper that several tools depend on.

The broader context matters too. CERT/CC's VU#221883, first released March 30 and last revised May 20, bundled four CrewAI CVEs, one of them a separate SSRF via improper URL validation in the RAG search tools (CVE-2026-2286), alongside two remote code execution findings and an arbitrary local file read. CrewAI's fetch and tool surface has drawn multiple independent SSRF findings across 2026. None of that makes the framework unusual; it makes it visible, which is the more useful property for anyone auditing their own tools against the same classes.

The five merged CrewAI pull requests with SSRF in the title between May and August 2026, with merge dates and what each one changed on the shared fetch path, plus the one open pull request that is not part of the 1.15.17 release.
Pull requestMergedWhat it changedAdvisory
#57112026-05-05Validate IPs on every redirect hop to prevent SSRF bypass.None found
#60382026-06-04Re-validate redirects and pin peer IP to close SSRF bypass.None found
#63312026-06-26Fix SSRF redirect bypass in scraping fetches. Shipped as 1.15.1.CVE-2026-62240 (published July 13; CVSS 4.0 8.3, CVSS 3.1 7.4)
#67952026-08-03Close SSRF in ArxivPaperTool.download_pdf, plus a DNS-rebinding gap in safe_get.None found
#69812026-08-17Pin SSRF checks to each redirect hop and peer IP. Shipped as 1.15.17 on August 20.None; single changelog line
#7001Open, not mergedProposes moving SSRF pinning inside an outbound-fetch module and removing the separate adapter stack. Not part of 1.15.17.Not applicable
Do not transfer the July score
CVE-2026-62240 and its 8.3 / 7.4 ratings describe the June redirect-bypass bug, fixed in 1.15.1. The August peer-IP pinning fix in 1.15.17 has no CVE, no GHSA and no severity of its own. They are sequential fixes to the same code path, five weeks apart by CVE publication and merge date, and should never be reported as one event. Nothing in the public record states that any CrewAI user was exploited through either gap; the original report was a controlled bug-bounty submission, not an incident.

04Why One-Time Validation FailsThe destination moves after the check.

None of this is specific to CrewAI, which is why the changelog line is worth generalising. The vulnerability class has a definition, a prevention cheat sheet, and a well-understood reason why checking the URL string once is not enough. The reason is that a URL is an instruction to the transport, not a destination. Between the moment your code approves it and the moment a packet leaves the host, two things can change it: the server on the other end can answer with a redirect, and the resolver can answer a second lookup with a different address.

CWE-918, as MITRE defines it
“The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.” In an agent, the upstream component is the model, and the model's input often came from a page it just read. The full CWE-918 entry is the canonical reference.

The OWASP SSRF Prevention Cheat Sheet makes three points that map directly onto the CrewAI history. First, its blunt instruction on redirects: “Disable the support for the following of the redirection in your web client in order to prevent the bypass of the input validation.” That single sentence is the whole of Design A's problem. Second, it warns that validating a domain name through DNS resolution “opens up the application to attacks” because an attacker can bind a legitimate domain name to an internal IP address, which is the rebinding class behind Design B's remaining gap. Third, its remediation for that gap is to resolve both A and AAAA records and apply the same validation at request time, not only at initial validation. PR #6981's getaddrinfo plus post-connect getpeername() sequence is one concrete implementation of the request-time half of that recommendation.

Library defaults decide how much of this you inherit without noticing. The two Python HTTP clients in play here, requests and httpx, disagree on the most important default of all.

requests
Follows redirects by default
allow_redirects=False to opt out

Per the requests quickstart, redirects are followed automatically for every HTTP verb except HEAD. A tool built on requests.get inherits Design A's behaviour unless it passes allow_redirects=False or validates each hop itself. This is the client CrewAI's safe_get wraps.

Default is the unsafe direction
httpx
Does not follow redirects by default
follow_redirects=True to opt in

Per the httpx quickstart, redirects are not followed for any method unless the caller explicitly passes follow_redirects=True. The safer default, which makes it notable when a tool turns it on, as the MCP reference fetch server does.

Default is the safe direction

Here is the interpretation we would offer. Every one of CrewAI's five hardening passes was an attempt to move validation from “a property of the URL string” to “a property of the connection.” The first passes re-validated more strings (each Location header). The last one stopped trusting strings at all and checked the socket. That is the right direction, and it is the direction OWASP's request-time guidance has pointed for years. What makes agent tools different from the classic web-app SSRF case is only the source of the URL: in a web app it is a user form field; in an agent it is whatever the model decided after reading untrusted content. The defence does not change. The likelihood that the input is adversarial goes up.

05The MCP Reference Fetch ServerZero SSRF checks in the reference implementation.

To see how one reference implementation compares, we read the source of the official MCP fetch server in the modelcontextprotocol/servers repository, src/fetch/src/mcp_server_fetch/server.py, at its most recent edit, dated March 15, 2026. It fetches through httpx with follow_redirects=True at two call sites and contains no SSRF-specific validation logic anywhere in the file: no private-IP check, no per-hop re-validation of redirect targets, no peer-IP pinning. Searching the file for the obvious terms (redirect, SSRF, validate, resolve, peer, metadata) turns up only the two redirect-following flags.

That is a precise, checkable claim, and it should be read precisely. The reference server is a reference, not a hardened product, and its maintainers may reasonably expect it to run behind network controls. But it is the fetch server the official reference repository ships, and it sits below Design A on the table above: one URL in, redirects followed, and no check at any point. Anyone running it against untrusted URLs from inside a private network or a cloud instance with a metadata endpoint is relying entirely on controls outside the tool.

Scope of this comparison
Only the MCP reference fetch server was checked directly for this post. The fetch and scrape tools in the OpenAI Agents SDK and in LangChain were not examined here, and nothing in this article should be read as a claim about them in either direction. If you depend on one, the checklist in Section 07 is the way to find out. Our 75-point MCP server security audit covers the wider server surface beyond fetch.

06Not the Context7 StoryTwo agent security stories, two attack surfaces.

Two days before this release, CVE-2026-75130 was published against the Context7 MCP server, and it is tempting to file both under “agent tooling had a bad week.” They are different classes and deserve different fixes. Context7's issue is prompt injection: the server delivers untrusted instructions into the agent's context on a routine documentation request, and the agent acts on them. CrewAI's issue is SSRF: the agent's own outbound fetch reaches a destination it was never meant to reach. One is an inbound content-trust problem solved by treating served content as data, not instructions; the other is an outbound network-egress problem solved by validating where a socket actually connects. They chain together naturally, since an injected instruction is one way a model ends up choosing a hostile URL, but they are not the same bug. We cover the Context7 advisory, its two divergent CVSS scores and its open patch status in our CVE-2026-75130 write-up, and the broader chain from injected content to tool misuse in our prompt-injection taxonomy for production agents.

Inbound · Context7
Prompt injection via served content
content → agent context → action

Untrusted instructions arrive inside a tool result and the agent treats them as guidance. Defence is a trust boundary on content: provenance, sanitisation and least-privilege tool scopes.

CVE-2026-75130 · Aug 18
Outbound · CrewAI
SSRF via a model-supplied URL
model → fetch tool → unintended destination

The agent makes a real request from inside your network to an address the validation never approved. Defence is a trust boundary on egress: per-hop validation and peer pinning.

1.15.17 · Aug 20 · no CVE

07Fetch-Tool SSRF Audit ChecklistSix checks for any tool that fetches a model-supplied URL.

This is the durable asset. Each row is a question you can answer about your own harness's fetch tool, grounded in OWASP's cheat sheet or CWE-918, with a mechanism-level way to test it in a lab you control. No working payloads, no target addresses beyond the classes OWASP itself names. The last two columns record what the primary sources say about CrewAI 1.15.17 and about the MCP reference fetch server.

A six-row audit checklist for agent fetch tools: each check, why it matters according to OWASP or CWE-918, how to test it at mechanism level, and the documented status of CrewAI 1.15.17 and of the MCP reference fetch server.
CheckWhy it mattersHow to test (lab only)CrewAI 1.15.17MCP reference fetch
Redirect handling
1 · Does the client follow redirects by default?OWASP: disable redirect-following to prevent bypass of input validation. requests follows by default; httpx does not.Point the tool at a URL you control that returns a 30x to a second URL you control. Confirm whether the second fetch happens and whether anything checked it.Follows, but re-validates each hop and pins the peer (PR #6981).Follows (follow_redirects=True), no re-validation found.
2 · Is each redirect's Location re-validated against the private/reserved blocklist?CWE-918: the request must be sent to the expected destination. A redirect changes the destination after the first check.Same lab setup, with the second URL's hostname resolving to a reserved range inside your lab. Expect refusal before any connection.Yes. Already in place before 1.15.17, per PR #6981's own description of safe_get.No validation code found.
Resolution and connection
3 · Is the IP actually connected to checked, or only the IP seen at validation time?OWASP: DNS-based validation alone is bypassable by binding a legitimate name to an internal address; apply validation at request time.In a lab resolver you control, serve a name whose answer differs between consecutive lookups. Expect either a refusal or a post-connect close.Yes. Resolve once, connect to that address, re-check getpeername() after connecting, close on failure.No. Hostname handed to httpx; no peer check.
4 · Are both A and AAAA answers validated?OWASP's remediation resolves both record types and validates every address, so an IPv6 answer cannot slip past an IPv4-only check.Serve a name with a harmless A record and a reserved-range AAAA record in your lab. Expect refusal.Not stated directly. The PR describes one getaddrinfo resolve and rejects if any returned address is private or reserved, which covers both record types unless the resolve is family-restricted.No validation code found.
Proxies and privileged destinations
5 · Can an HTTP proxy substitute the connected peer, including via environment variables?With a proxy in the path the socket's peer is the proxy, and any peer-level check sees the wrong address (PR #6981).Set the standard proxy environment variables in the tool's process and confirm the fetch ignores them, or refuses.Locked down: trust_env=False, proxies empty, non-empty proxies rejected unless an explicit escape hatch is on.No proxy handling found in server.py.
6 · Are cloud-metadata, link-local and loopback destinations explicitly denied?Internal services and cloud metadata endpoints are the classic SSRF targets in the CWE-918 class; a blocklist that misses link-local ranges misses them.From a non-cloud lab host, attempt a fetch to a link-local or loopback address. Expect refusal before any connection is opened.Private/reserved blocklist applied at resolve and post-connect. Confirm link-local coverage on your installed version.No denylist found.

What to do with the answers depends on which side of the table you are on.

You run CrewAI
Upgrade, and leave the escape hatch closed

Move to 1.15.17 or later. The proxies escape hatch exists for a reason, but once a proxy is in the path the connected peer is the proxy rather than the destination, which is the vector the PR describes; if you need an egress proxy, prefer the network layer over the fetch session.

Upgrade to 1.15.17
You build on requests
Treat the default as unsafe

requests follows redirects for every verb except HEAD. Either pass allow_redirects=False and handle hops yourself with validation on each, or adopt the resolve-once, connect-to-address, re-check-peer pattern PR #6981 documents.

Per-hop validation plus peer pinning
You build on httpx
Do not flip the default casually

httpx does not follow redirects unless you ask. The moment a tool sets follow_redirects=True it inherits the whole redirect problem, which is exactly what the MCP reference fetch server does. Keep the default, or pair the flag with per-hop checks.

Keep follow_redirects off
You run the MCP reference fetch server
Assume no validation and isolate it

Its source carries no SSRF checks at its most recent edit. Run it where there is nothing internal to reach, behind an egress allowlist, and never on a host that can see a cloud metadata endpoint or a private network you care about.

Network-level isolation

08What It Means for TeamsTreat fetch tools as network egress, not string parsing.

The quiet way this fix shipped is itself the lesson. A team that relies on advisories and scanner alerts to know when to upgrade would never hear about 1.15.17's SSRF change, because there is no advisory to alert on. Meanwhile the same team's own fetch tool, the one a contractor wrote in an afternoon on top of requests.get, has never been audited against any of the six rows above. The useful response is not to worry about CrewAI specifically. It is to recognise that any tool which takes a URL from a model and opens a socket is a piece of network egress infrastructure, and to review it the way you would review a proxy or a firewall rule, not the way you would review a string parser.

Looking forward, we expect two things. First, more framework changelogs like this one: hardening passes that land as single lines because they close a design weakness rather than a reported exploit, with no CVE to anchor coverage. Reading release notes for your agent dependencies, not just your web framework, becomes a real operational habit. Second, we expect the validate-and-pin pattern to migrate from framework internals into a shared layer, an egress sidecar or a hardened fetch library, because five incremental passes on one helper is the kind of history that eventually convinces maintainers to stop re-implementing the same guard per tool. The still-open PR #7001, which proposes moving the pinning into a single outbound-fetch module, points in that direction, though nothing about it has shipped.

For teams choosing a framework, this record is a point in CrewAI's favour, not against it: the fixes are public, dated and attributable, which is more than most tools offer. Our comparison of LangGraph and CrewAI for agentic orchestration covers the architectural trade-offs. For the wider picture of how often agentic systems now feature in real breach data, see our analysis of agentic systems in 2026 breach reporting, and for the server-side controls that should sit around any fetch tool, our MCP server security engineering guide. When we build agent systems for clients through our AI transformation engagements and custom web development work, the fetch-tool audit above is part of the standard review, precisely because the tools that look simplest are the ones nobody checks.

09ConclusionValidate the connection, not the string.

The pattern behind one changelog line

A URL is an instruction to the transport, not a destination. Check where the socket actually lands.

CrewAI 1.15.17 pins SSRF checks to each redirect hop and to the resolved peer IP, and it did so without an advisory, a CVE or a severity rating. The gap it closes is narrower than the headline: by August the framework already re-validated every redirect's Location header, and what remained was the mismatch between the address that passed validation and the address the socket dialled, plus a proxy that could stand in for the peer. The earlier, broader “only the first URL was checked” bug was real, was fixed in June, and carries its own CVE. Keep them separate.

The generalisable part is the fix, not the framework. Resolve once, reject private and reserved answers, connect to the validated address rather than the hostname, re-check the peer after the socket opens, and refuse to inherit a proxy from the environment. OWASP has recommended request-time validation for this class for years; PR #6981 is a working, readable implementation of it in ordinary Python. The MCP reference fetch server, by contrast, has none of it, and it is the only other fetch tool we read for this post.

Run the six checks against your own tools this week. Most of them take minutes in a lab you control, and the answers will tell you whether your agent's fetch capability is a validated egress path or a string that gets handed to a transport and hoped for.

Harden your agent tooling

The tools that look simplest are the ones nobody checks.

We audit and harden agent tool surfaces, fetch tools, MCP servers and orchestration frameworks, against the SSRF, prompt-injection and egress-control classes that ship as changelog lines rather than advisories.

Free consultationExpert guidanceTailored solutions
What we work on

Agent security engagements

  • Fetch-tool SSRF audits against the six-row checklist
  • MCP server trust-boundary and egress reviews
  • Framework selection and upgrade cadence for agent stacks
  • Prompt-injection to tool-misuse chain analysis
  • Hardened fetch layers for custom agent builds
FAQ · CrewAI SSRF fix

The questions we get every week.

CrewAI 1.15.17, published August 20, 2026, includes pull request #6981, merged August 17, whose release-notes line reads “Pin SSRF checks to each redirect hop and peer IP.” The change makes the shared safe-fetch helper resolve a hostname once, reject the request if any returned address is private or reserved, connect directly to the validated address rather than the hostname, re-check the connected peer with getpeername() after the socket opens, and close the socket if that second check fails. The same PR sets trust_env=False and an empty proxies map on the fetch session and rejects non-empty proxies unless an explicit escape hatch is enabled. The diff is 767 additions and 70 deletions across 19 files, including a new SSRF adapter module, the docs-site loader and the URL read tool.
Related dispatches

Continue exploring agent security.