* fix(community): return an error for malformed URLs in the SSRF guard
validate_public_http_url and validate_delegated_backend_url document
returning an "Error: ..." string, but urlparse() raises ValueError for
malformed authorities such as http://[::1/api, so the exception escaped
to callers (web_fetch/web_capture, browser_navigate, personal MCP, and
the browser stream navigation check) instead of a clean validation error.
Parse through a small _parse_url helper that returns None for malformed
input (including non-string values), and treat a raw bracket in the
userinfo slot as malformed on every Python version, mirroring the
SkillScan host parser merged in #8017.
The only URLs newly rejected are userinfo values containing a raw
bracket, which RFC 3986 forbids; no valid host becomes unreachable and
fail-closed behavior is otherwise unchanged.
* test(community): share malformed URL cases and add changelog entry
Drive both validators' malformed-URL parametrization from one list, and
document the contract fix in the Unreleased Fixed section (#8079).
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
* Fail closed on delegated fetch backends to close SSRF gap
* fix(community): resolve delegated-fetch credentials from one config snapshot
Address review feedback on the fail-closed SSRF guard:
- firecrawl/fastcrw client construction no longer re-reads get_app_config(),
so the API key and endpoint come from the same revision the endpoint screen
validated (prevents cross-revision credential misrouting).
- fix the browserless test double to accept the new two-argument
_get_browserless_client signature.
- document the check-only (DNS rebinding) caveat on
validate_delegated_backend_url.
- add a CHANGELOG upgrade note for the default self-hosted backend break.
* fix(doctor): consolidate delegated fetch backend checks and deployment guidance
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
* fix(community): refuse non-global addresses in the shared SSRF guard
is_blocked_address ORed is_private, is_loopback, is_link_local,
is_reserved, is_multicast and is_unspecified. None of them covers the
100.64.0.0/10 shared address space, which holds CGNAT and Tailscale
hosts and Alibaba Cloud's 100.100.100.200 instance metadata endpoint, so
web_fetch, web_capture, the agentic browser and personal MCP connections
could reach it, including through the IPv4-mapped ::ffff:100.100.100.200
form a DNS answer can carry.
Also refuse any address that is not is_global, the classification the
repo's other SSRF filters already use. Keep the existing flags alongside
it: some non-public forms still report is_global, such as the NAT64
spelling of a metadata address (64:ff9b::a9fe:a9fe) and 224.0.0.1, so an
is_global-only check would newly allow them.
* docs(changelog): reference #6202 in the shared address space entry
* test(community): pin the IPv4-mapped metadata spelling at the web_fetch and personal MCP entry points
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>