AI Integration · BraivIQ AI Engineering Playbook
Agent Web Fetch Security: Limits Before Parsing, Redaction Without Backtracking, Clean Redirects
Agent web fetch security comes down to doing the cheap checks first. On 4 November 2026 Pydantic AI 2.66.0 and 1.107.10 fixed a web fetch that re-indented JSON before applying its content limit and a secret redaction pattern that backtracked exponentially. On 30 October follow-redirects 1.16.2 fixed a bug that could forward a custom credential header to a cross-origin redirect. This playbook shows the three guard rails in tested Python: size and depth limits before parsing, a linear scan for redaction, and credential headers dropped on any cross-origin redirect.
Published · Updated · 10 min read · By BraivIQ Engineering
Key takeaways
- Pydantic AI 2.66.0 and 1.107.10 (4 November 2026) fixed GHSA-m5vj-8mcw-82qx, where a fetched JSON response was re-indented before the content limit applied.
- The same releases fixed GHSA-mfx5-pc32-32m8, a private key redaction pattern that backtracked exponentially on crafted text.
- follow-redirects 1.16.2 (30 October 2026) fixed CVE-2026-116689, which could forward a custom credential header to a cross-origin redirect.
- Check size and nesting before any parse or reformat, redact with a linear scan, and drop credential headers on every cross-origin redirect.
Agent web fetch security matters because the agent, not your team, picks many of the URLs it reads, and anyone who can influence those URLs can influence what the tool processes. Three advisories in a week show where the work goes wrong: reshaping content before limiting it, redacting with patterns that can be made slow, and forwarding credentials when a server redirects elsewhere.
What did the advisories disclose?
The Pydantic AI 2.66.0 release notes, published on 4 November 2026, describe GHSA-m5vj-8mcw-82qx (high): web_fetch_tool and the WebFetch capability's local fallback re-indented a JSON response on the event loop before applying the returned-content limit, so a deeply nested document within the download size limit could expand to hundreds of times its size. The notes say you are affected if an attacker can choose or influence the URLs the tool fetches, and that provider-native web fetching is not affected. Fixed in 2.66.0 and 1.107.10.
The same release lists GHSA-mfx5-pc32-32m8 (high): a private key pattern in the project's secret redaction backtracked exponentially on crafted text, so a few hundred characters that begin like a private key could occupy the event loop. Separately, CVE-2026-116689 in follow-redirects 1.16.0 and 1.16.1, published on 30 October 2026, meant an array of sensitive headers created in another JavaScript realm was replaced with an empty list, so a custom credential header could be forwarded to an attacker-controlled cross-origin redirect. Version 1.16.2 fixes it.
What does agent web fetch security look like as a design?
How do you build the three guard rails?
safe_fetch.py"""Guard rails for an agent's web fetch tool: limits before transforms, linear-time redaction, and no
credential headers across origins.
Pydantic AI 2.66.0 and 1.107.10 (4 November 2026) fixed a web fetch that re-indented JSON before applying
the content limit (GHSA-m5vj-8mcw-82qx) and a redaction pattern that backtracked exponentially
(GHSA-mfx5-pc32-32m8). follow-redirects 1.16.2 (30 October 2026) fixed credential headers forwarded to a
cross-origin redirect (CVE-2026-116689). Python 3.14 standard library only.
"""
from __future__ import annotations
import json
from urllib.parse import urlsplit
MAX_BYTES, MAX_DEPTH = 200_000, 20
SENSITIVE = {"authorization", "cookie", "x-api-key", "proxy-authorization"}
def parse_limited(raw: bytes) -> object:
if len(raw) > MAX_BYTES:
raise ValueError("response over the byte limit, refused before parsing")
depth = 0
for ch in raw[:MAX_BYTES].decode("utf-8", "replace"):
depth += ch in "[{"
depth -= ch in "]}"
if depth > MAX_DEPTH:
raise ValueError(f"nesting deeper than {MAX_DEPTH}, refused before parsing")
return json.loads(raw) # never re-indent or pretty-print before these checks
def redact_keys(text: str) -> str:
"""Linear scan for PEM blocks, no regex backtracking."""
start, out = 0, []
while (i := text.find("-----BEGIN ", start)) != -1:
j = text.find("-----END ", i)
k = text.find("-----", j + 9) if j != -1 else -1
out.append(text[start:i] + "[redacted key]")
start = k + 5 if k != -1 else len(text)
return "".join(out) + text[start:]
def redirect_headers(headers: dict[str, str], from_url: str, to_url: str) -> dict[str, str]:
a, b = urlsplit(from_url), urlsplit(to_url)
same = (a.scheme, a.hostname, a.port) == (b.scheme, b.hostname, b.port)
return headers if same else {k: v for k, v in headers.items() if k.lower() not in SENSITIVE}
if __name__ == "__main__":
try:
parse_limited(b"[" * 50 + b"]" * 50)
except ValueError as e:
print("deep json:", e)
print("redacted:", redact_keys("key: -----BEGIN PRIVATE KEY-----abc-----END PRIVATE KEY----- done"))
print("pathological input:", len(redact_keys("-----BEGIN " * 5000)), "chars, returned at once")
h = {"Authorization": "Bearer t", "X-Api-Key": "k", "Accept": "application/json"}
print("cross-origin:", redirect_headers(h, "https://api.example.com/a", "https://evil.example.net/b"))Python 3.14.4, standard library only. Run once as shown. Pure functions, no network calls.
A fifty-level nested array is refused before parsing because nesting passes the limit of twenty. A PEM block is replaced by a placeholder with a linear scan, and five thousand repeated private key openings return at once, because nothing backtracks. On a redirect from one host to another, the Authorization and X-Api-Key headers are dropped and only Accept is kept.
What breaks in production?
- **Pretty-printing for the model.** Formatting a response to make it readable can multiply its size. Limit first, format second, and cap again after formatting.
- **Redaction patterns copied from the internet.** Patterns with nested repetition can be made slow. Test every pattern against long crafted input.
- **Redirect handling in the HTTP library.** Know which headers your client forwards, and upgrade follow-redirects to 1.16.2 or later if you use it.
- **Fetched text as instructions.** Mark everything fetched as data. Our prompt injection defence playbook covers why.
What are the alternatives to a local fetch tool?
| Option | Good fit | Trade-off |
|---|---|---|
| Provider-native web fetching | Public pages, no internal credentials | Less control over limits and logging |
| Local fetch tool with guard rails, as above | Approved hosts and internal APIs | You own the limits and the tests |
| No web access, curated documents only | Regulated workflows | The agent only knows what you load |
Our playbook on keeping tokens out of CI logs covers redaction elsewhere, and our blog gives the AI agent security questions to ask vendors.
Where should a team start?
Upgrade Pydantic AI and follow-redirects if you use them, then test your own fetch tool with a deeply nested response, a long crafted key prefix and a cross-origin redirect. As a Workflow Automation Agency we build these tests into every agent, as described on our services page, and a 14-day Proof Run includes them.
Frequently asked questions
Which Pydantic AI versions fix the November 2026 web fetch advisory?
Pydantic AI 2.66.0 and 1.107.10, released on 4 November 2026, fix GHSA-m5vj-8mcw-82qx and GHSA-mfx5-pc32-32m8, for both pydantic-ai and pydantic-ai-slim.
What is CVE-2026-116689?
A follow-redirects bug in 1.16.0 and 1.16.1, published on 30 October 2026, that could forward a custom credential header to a cross-origin redirect. Version 1.16.2 fixes it.
Why apply limits before parsing a fetched response?
Because parsing or reformatting can multiply the size of a small, deeply nested response. Checking bytes and nesting first stops the expansion before it starts.
References
- Pydantic (GitHub), "pydantic-ai v2.66.0 release notes", 4 November 2026. https://github.com/pydantic/pydantic-ai/releases/tag/v2.66.0
- Pydantic (GitHub), "GHSA-m5vj-8mcw-82qx: web fetch re-indented JSON before the content limit", 4 November 2026. https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-m5vj-8mcw-82qx
- GitHub Advisory Database, "follow-redirects: cross-realm sensitiveHeaders arrays discarded, credentials forwarded (CVE-2026-116689)", 30 October 2026. https://github.com/advisories/GHSA-mrwp-f3q8-3g9p