mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* 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>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""Address classification shared by every SSRF-screened web tool."""
|
|
|
|
import ipaddress
|
|
|
|
import pytest
|
|
|
|
from deerflow.community.url_safety import is_blocked_address, validate_public_http_url
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"address",
|
|
[
|
|
"100.64.0.0",
|
|
"100.64.1.1", # CGNAT / Tailscale node
|
|
"100.100.100.200", # Alibaba Cloud ECS instance metadata
|
|
"100.127.255.255",
|
|
"::ffff:100.100.100.200", # IPv4-mapped spelling of the same endpoint
|
|
],
|
|
)
|
|
def test_shared_address_space_is_blocked(address):
|
|
assert is_blocked_address(ipaddress.ip_address(address))
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"address",
|
|
[
|
|
"127.0.0.1",
|
|
"10.0.0.5",
|
|
"169.254.169.254",
|
|
"0.0.0.0",
|
|
"224.0.0.1",
|
|
"::1",
|
|
"fe80::1",
|
|
"fc00::1",
|
|
"::ffff:127.0.0.1",
|
|
# NAT64 spelling of a metadata address reports is_global, so the
|
|
# reserved/private flags must keep applying alongside it.
|
|
"64:ff9b::a9fe:a9fe",
|
|
],
|
|
)
|
|
def test_previously_blocked_addresses_stay_blocked(address):
|
|
assert is_blocked_address(ipaddress.ip_address(address))
|
|
|
|
|
|
@pytest.mark.parametrize("address", ["8.8.8.8", "93.184.215.14", "100.63.255.255", "100.128.0.0", "2606:4700:4700::1111"])
|
|
def test_public_addresses_stay_reachable(address):
|
|
assert not is_blocked_address(ipaddress.ip_address(address))
|
|
|
|
|
|
def test_validator_refuses_a_hostname_resolving_into_shared_address_space():
|
|
error = validate_public_http_url("http://metadata.example/latest/meta-data/", resolver=lambda _host: [ipaddress.ip_address("100.100.100.200")])
|
|
|
|
assert error == "Error: Refusing to fetch a private, loopback, or metadata address"
|