You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(tools): promote the resolved-IP SSRF re-check into a reusable primitive
is_safe_url is only syntactic; the DNS-rebinding re-check (re-resolve each hop,
refuse a private/reserved address) was left to each consumer's fetcher — so every
consumer re-derived it, and each independently broke behind a fake-IP proxy.
Add rlm_kit.tools.resolved_host_is_safe(host, port, *, allow_nets=()) + parse_cidrs:
the resolved-address guard now ships once, with an allow_nets carve-out for a host
behind a fake-IP proxy / split-DNS VPN (Clash/Mihomo/Surge map every public host into
the reserved 198.18.0.0/16, which the strict re-check would refuse — starving the model
of all fetched source). Empty allow_nets = unchanged strictness: is_safe_url still
refuses localhost/metadata regardless of allow_nets.
_hostname_is_blocked_literal_ip and the new _ip_blocked share one _ip_in_blocked_range,
so the block-category list has a single home. Additive public surface (tools __all__);
no schema/contract change. Consumer-driven hardening — surfaced by a downstream direct
fetcher refusing every host behind such a proxy.
166 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments