mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-25 05:54:53 +00:00
Merge 3bc5ac0445 into 82ca10cc2d
This commit is contained in:
commit
f44e60f618
4 changed files with 1066 additions and 41 deletions
|
|
@ -5,3 +5,4 @@
|
|||
- Apply provider-supplied header decisions without importing provider implementations
|
||||
- Preserve existing header precedence, duplicate handling, and unrelated forwarded headers
|
||||
- Verify observable request behavior rather than the structure of helper functions
|
||||
- The media SSRF blocklist in `media.rs` follows the IANA special-purpose registries and is a superset of the Python `_is_blocked_ip` policy. `generated/blocked_ips.json` is CPython output from `scripts/generate_blocked_ips.py`, never hand-edited. Regenerate it when the Python policy or probe list changes, and record a Rust-only block in `KNOWN_OVER_BLOCKED` with its reason
|
||||
|
|
|
|||
697
litellm-rust/crates/http/generated/blocked_ips.json
Normal file
697
litellm-rust/crates/http/generated/blocked_ips.json
Normal file
|
|
@ -0,0 +1,697 @@
|
|||
{
|
||||
"python": "3.12.13",
|
||||
"rows": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "0.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "1.0.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "9.255.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "10.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "10.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "11.0.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "100.63.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "100.64.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "100.127.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "100.128.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "126.255.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "127.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "128.0.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "168.63.129.15",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "168.63.129.16",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "168.63.129.17",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "169.253.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "169.254.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "169.254.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "169.255.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "172.15.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "172.16.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "172.31.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "172.32.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "191.255.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.1.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.8",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.9",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.10",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.11",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.169",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.170",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.171",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.0.172",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.1.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.0.2.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.2.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.0.3.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.31.195.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.31.196.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.31.196.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.31.197.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.52.192.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.52.193.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.52.193.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.52.194.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.88.98.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.88.99.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.88.99.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.88.100.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.167.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.168.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.168.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "192.169.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.175.47.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.175.48.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.175.48.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "192.175.49.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "198.17.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "198.18.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "198.19.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "198.20.0.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "198.51.99.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "198.51.100.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "198.51.100.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "198.51.101.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "203.0.112.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "203.0.113.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "203.0.113.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "203.0.114.0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "223.255.255.255",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "224.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "239.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "240.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "255.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "255.255.255.254",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::1",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::2",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "::fffe:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "::ffff:0.0.0.0",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::ffff:255.255.255.255",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::1:0:0:0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9a:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b::ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b::1:0:0",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b:0:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b:1::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b:1:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b:2::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "ff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "100::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "100::ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "100:0:0:1::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:200::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:0:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:1::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:1::1",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:1::2",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:1::3",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:1:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:2::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:2:0:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:2:1::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:2:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:3::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:3:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:4::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:4:111:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:4:112::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:4:112:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:4:113::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:f:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:10::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:1f:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:20::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:2f:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:30::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:3f:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:40::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:db7:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:db8::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2001:db9::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2002::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2003::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "3ffe:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "3fff::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "3fff:1000::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "5eff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "5f00::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "5f00:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "5f01::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "fc00::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "fe00::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "fe80::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "fec0::",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "ff00::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "8.8.8.8",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "1.1.1.1",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "100.128.0.1",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2606:4700::1111",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:200::1",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2001:4860:4860::8888",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "2002:c000:204::1",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2002:0a00:1::",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "2002:808:808::1",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::ffff:127.0.0.1",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::ffff:168.63.129.16",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::ffff:10.0.0.1",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::ffff:8.8.8.8",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "::ffff:169.254.169.254",
|
||||
"blocked": true
|
||||
},
|
||||
{
|
||||
"address": "::127.0.0.1",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "::8.8.8.8",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b::808:808",
|
||||
"blocked": false
|
||||
},
|
||||
{
|
||||
"address": "64:ff9b::a00:1",
|
||||
"blocked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
121
litellm-rust/crates/http/scripts/generate_blocked_ips.py
Normal file
121
litellm-rust/crates/http/scripts/generate_blocked_ips.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
"""Regenerate generated/blocked_ips.json: what the Python SSRF policy says for each probe.
|
||||
|
||||
uv run --no-sync python litellm-rust/crates/http/scripts/generate_blocked_ips.py \
|
||||
> litellm-rust/crates/http/generated/blocked_ips.json
|
||||
|
||||
Each row records whether ``litellm.litellm_core_utils.url_utils._is_blocked_ip`` blocks the
|
||||
address under the interpreter named in the header. The verdicts depend on that interpreter's
|
||||
``ipaddress`` tables, so the file is regenerated, never hand-edited.
|
||||
"""
|
||||
|
||||
import json
|
||||
import platform
|
||||
import sys
|
||||
from ipaddress import ip_network
|
||||
from typing import Final
|
||||
|
||||
from litellm.litellm_core_utils.url_utils import _is_blocked_ip
|
||||
|
||||
# IANA special-purpose ranges plus the cloud-fabric exceptions, probed at both edges and one
|
||||
# address past each edge so a prefix length that is off by one shows up.
|
||||
NETWORKS: Final = (
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"127.0.0.0/8",
|
||||
"168.63.129.16/32",
|
||||
"169.254.0.0/16",
|
||||
"172.16.0.0/12",
|
||||
"192.0.0.0/24",
|
||||
"192.0.0.9/32",
|
||||
"192.0.0.10/32",
|
||||
"192.0.0.170/31",
|
||||
"192.0.2.0/24",
|
||||
"192.31.196.0/24",
|
||||
"192.52.193.0/24",
|
||||
"192.88.99.0/24",
|
||||
"192.168.0.0/16",
|
||||
"192.175.48.0/24",
|
||||
"198.18.0.0/15",
|
||||
"198.51.100.0/24",
|
||||
"203.0.113.0/24",
|
||||
"224.0.0.0/4",
|
||||
"240.0.0.0/4",
|
||||
"255.255.255.255/32",
|
||||
"::/128",
|
||||
"::1/128",
|
||||
"::ffff:0:0/96",
|
||||
"64:ff9b::/96",
|
||||
"64:ff9b:1::/48",
|
||||
"100::/64",
|
||||
"2001::/23",
|
||||
"2001::/32",
|
||||
"2001:1::1/128",
|
||||
"2001:1::2/128",
|
||||
"2001:2::/48",
|
||||
"2001:3::/32",
|
||||
"2001:4:112::/48",
|
||||
"2001:10::/28",
|
||||
"2001:20::/28",
|
||||
"2001:30::/28",
|
||||
"2001:db8::/32",
|
||||
"2002::/16",
|
||||
"3fff::/20",
|
||||
"5f00::/16",
|
||||
"fc00::/7",
|
||||
"fe80::/10",
|
||||
"fec0::/10",
|
||||
"ff00::/8",
|
||||
)
|
||||
|
||||
# Addresses inside the ranges that the module's own tests name, plus mapped and compat forms.
|
||||
SPOT_CHECKS: Final = (
|
||||
"8.8.8.8",
|
||||
"1.1.1.1",
|
||||
"168.63.129.17",
|
||||
"100.128.0.1",
|
||||
"223.255.255.255",
|
||||
"2606:4700::1111",
|
||||
"2001:200::1",
|
||||
"2001:4860:4860::8888",
|
||||
"2002:c000:204::1",
|
||||
"2002:0a00:1::",
|
||||
"2002:808:808::1",
|
||||
"::ffff:127.0.0.1",
|
||||
"::ffff:168.63.129.16",
|
||||
"::ffff:10.0.0.1",
|
||||
"::ffff:8.8.8.8",
|
||||
"::ffff:169.254.169.254",
|
||||
"::127.0.0.1",
|
||||
"::8.8.8.8",
|
||||
"64:ff9b::808:808",
|
||||
"64:ff9b::a00:1",
|
||||
)
|
||||
|
||||
|
||||
def _edges(network: str) -> tuple[str, ...]:
|
||||
net: Final = ip_network(network)
|
||||
first: Final = net.network_address
|
||||
last: Final = net.broadcast_address
|
||||
below: Final = first - 1 if int(first) > 0 else None
|
||||
above: Final = last + 1 if int(last) < 2**net.max_prefixlen - 1 else None
|
||||
return tuple(str(address) for address in (below, first, last, above) if address is not None)
|
||||
|
||||
|
||||
def _probes() -> tuple[str, ...]:
|
||||
edges: Final = tuple(address for network in NETWORKS for address in _edges(network))
|
||||
return tuple(dict.fromkeys(edges + SPOT_CHECKS))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
rows: Final = tuple({"address": address, "blocked": _is_blocked_ip(address)} for address in _probes())
|
||||
json.dump(
|
||||
{"python": platform.python_version(), "rows": rows},
|
||||
sys.stdout,
|
||||
indent=2,
|
||||
)
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{
|
||||
future::Future,
|
||||
io,
|
||||
net::{IpAddr, SocketAddr},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
|
|
@ -287,38 +287,59 @@ fn validate_addresses(addresses: &[SocketAddr]) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Ranges the IANA special-purpose registries mark as not globally reachable, plus multicast
|
||||
/// and the Azure Wire Server. A superset of what `_is_blocked_ip` in
|
||||
/// `litellm/litellm_core_utils/url_utils.py` rejects, pinned by `generated/blocked_ips.json`.
|
||||
const BLOCKED_V4: [(Ipv4Addr, u32); 15] = [
|
||||
(Ipv4Addr::new(0, 0, 0, 0), 8),
|
||||
(Ipv4Addr::new(10, 0, 0, 0), 8),
|
||||
(Ipv4Addr::new(100, 64, 0, 0), 10),
|
||||
(Ipv4Addr::new(127, 0, 0, 0), 8),
|
||||
(Ipv4Addr::new(168, 63, 129, 16), 32),
|
||||
(Ipv4Addr::new(169, 254, 0, 0), 16),
|
||||
(Ipv4Addr::new(172, 16, 0, 0), 12),
|
||||
(Ipv4Addr::new(192, 0, 0, 0), 24),
|
||||
(Ipv4Addr::new(192, 0, 2, 0), 24),
|
||||
(Ipv4Addr::new(192, 88, 99, 0), 24),
|
||||
(Ipv4Addr::new(192, 168, 0, 0), 16),
|
||||
(Ipv4Addr::new(198, 18, 0, 0), 15),
|
||||
(Ipv4Addr::new(198, 51, 100, 0), 24),
|
||||
(Ipv4Addr::new(203, 0, 113, 0), 24),
|
||||
(Ipv4Addr::new(224, 0, 0, 0), 3),
|
||||
];
|
||||
|
||||
const BLOCKED_V6: [(Ipv6Addr, u32); 13] = [
|
||||
(Ipv6Addr::UNSPECIFIED, 128),
|
||||
(Ipv6Addr::LOCALHOST, 128),
|
||||
(Ipv6Addr::new(0x64, 0xff9b, 1, 0, 0, 0, 0, 0), 48),
|
||||
(Ipv6Addr::new(0x100, 0, 0, 0, 0, 0, 0, 0), 64),
|
||||
(Ipv6Addr::new(0x2001, 0, 0, 0, 0, 0, 0, 0), 23),
|
||||
(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32),
|
||||
(Ipv6Addr::new(0x2002, 0, 0, 0, 0, 0, 0, 0), 16),
|
||||
(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0), 20),
|
||||
(Ipv6Addr::new(0x5f00, 0, 0, 0, 0, 0, 0, 0), 16),
|
||||
(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0), 7),
|
||||
(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 10),
|
||||
(Ipv6Addr::new(0xfec0, 0, 0, 0, 0, 0, 0, 0), 10),
|
||||
(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 8),
|
||||
];
|
||||
|
||||
fn in_network<const BITS: u32>(address: u128, network: u128, prefix: u32) -> bool {
|
||||
(address ^ network).checked_shr(BITS - prefix).unwrap_or(0) == 0
|
||||
}
|
||||
|
||||
fn is_blocked_ip(ip: IpAddr) -> bool {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => {
|
||||
let [first, second, third, _] = ip.octets();
|
||||
first == 0
|
||||
|| first == 10
|
||||
|| first == 127
|
||||
|| (first == 100 && (64..=127).contains(&second))
|
||||
|| (first == 169 && second == 254)
|
||||
|| (first == 172 && (16..=31).contains(&second))
|
||||
|| (first == 192 && second == 0 && (third == 0 || third == 2))
|
||||
|| (first == 192 && second == 168)
|
||||
|| (first == 192 && second == 88 && third == 99)
|
||||
|| (first == 198 && (second == 18 || second == 19))
|
||||
|| (first == 198 && second == 51 && third == 100)
|
||||
|| (first == 203 && second == 0 && third == 113)
|
||||
|| first >= 224
|
||||
}
|
||||
IpAddr::V4(ip) => BLOCKED_V4.iter().any(|(network, prefix)| {
|
||||
in_network::<32>(u32::from(ip).into(), u32::from(*network).into(), *prefix)
|
||||
}),
|
||||
IpAddr::V6(ip) => {
|
||||
let segments = ip.segments();
|
||||
ip.is_loopback()
|
||||
|| ip.is_unspecified()
|
||||
|| ip.is_multicast()
|
||||
|| (segments[0] & 0xfe00) == 0xfc00
|
||||
|| (segments[0] & 0xffc0) == 0xfe80
|
||||
|| (segments[0] & 0xffc0) == 0xfec0
|
||||
|| (segments[0] == 0x2001 && segments[1] == 0x0db8)
|
||||
|| ip
|
||||
.to_ipv4_mapped()
|
||||
.or_else(|| ip.to_ipv4())
|
||||
.map(|ipv4| is_blocked_ip(IpAddr::V4(ipv4)))
|
||||
.unwrap_or(false)
|
||||
BLOCKED_V6.iter().any(|(network, prefix)| {
|
||||
in_network::<128>(u128::from(ip), u128::from(*network), *prefix)
|
||||
}) || ip
|
||||
.to_ipv4_mapped()
|
||||
.or_else(|| ip.to_ipv4())
|
||||
.is_some_and(|ipv4| is_blocked_ip(IpAddr::V4(ipv4)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -433,19 +454,17 @@ mod tests {
|
|||
|
||||
struct TestAddressResolver {
|
||||
blocked_hosts: HashSet<&'static str>,
|
||||
blocked_ip: IpAddr,
|
||||
}
|
||||
|
||||
impl AddressResolver for TestAddressResolver {
|
||||
fn resolve<'a>(&'a self, host: &'a str, port: u16) -> AddressResolution<'a> {
|
||||
let blocked = self.blocked_hosts.contains(host);
|
||||
Box::pin(async move {
|
||||
let ip = if blocked {
|
||||
IpAddr::from([127, 0, 0, 1])
|
||||
} else {
|
||||
IpAddr::from([8, 8, 8, 8])
|
||||
};
|
||||
Ok(vec![SocketAddr::new(ip, port)])
|
||||
})
|
||||
let ip = if self.blocked_hosts.contains(host) {
|
||||
self.blocked_ip
|
||||
} else {
|
||||
IpAddr::from([8, 8, 8, 8])
|
||||
};
|
||||
Box::pin(async move { Ok(vec![SocketAddr::new(ip, port)]) })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,13 +480,32 @@ mod tests {
|
|||
blocked_hosts: HashSet<&'static str>,
|
||||
url_policy: UrlPolicy,
|
||||
uses_proxy: bool,
|
||||
) -> MediaFetcher {
|
||||
fetcher_resolving_blocked_hosts_to(
|
||||
pinned_address,
|
||||
blocked_hosts,
|
||||
IpAddr::from([127, 0, 0, 1]),
|
||||
url_policy,
|
||||
uses_proxy,
|
||||
)
|
||||
}
|
||||
|
||||
fn fetcher_resolving_blocked_hosts_to(
|
||||
pinned_address: SocketAddr,
|
||||
blocked_hosts: HashSet<&'static str>,
|
||||
blocked_ip: IpAddr,
|
||||
url_policy: UrlPolicy,
|
||||
uses_proxy: bool,
|
||||
) -> MediaFetcher {
|
||||
let direct = Resolution::from(&HttpSettings::default()).config;
|
||||
MediaFetcher::with_resolution(
|
||||
&HttpClientPool::new(Arc::new(LoopbackDnsResolver(pinned_address))),
|
||||
&direct,
|
||||
url_policy,
|
||||
Arc::new(TestAddressResolver { blocked_hosts }),
|
||||
Arc::new(TestAddressResolver {
|
||||
blocked_hosts,
|
||||
blocked_ip,
|
||||
}),
|
||||
Arc::new(move |_| uses_proxy),
|
||||
)
|
||||
.expect("test fetcher builds")
|
||||
|
|
@ -499,23 +537,167 @@ mod tests {
|
|||
#[case::documentation_v4_first_range("198.51.100.1")]
|
||||
#[case::documentation_v4_second_range("203.0.113.1")]
|
||||
#[case::multicast_v4("224.0.0.1")]
|
||||
#[case::broadcast_v4("255.255.255.255")]
|
||||
#[case::ietf_protocol_assignments_v4("192.0.0.9")]
|
||||
#[case::azure_wire_server("168.63.129.16")]
|
||||
#[case::unspecified_v6("::")]
|
||||
#[case::loopback_v6("::1")]
|
||||
#[case::local_use_nat64("64:ff9b:1::1")]
|
||||
#[case::discard_only_v6("100::1")]
|
||||
#[case::teredo("2001::1")]
|
||||
#[case::documentation_v6("2001:db8::1")]
|
||||
#[case::six_to_four("2002:c000:204::1")]
|
||||
#[case::documentation_v6_second_range("3fff::1")]
|
||||
#[case::segment_routing_sids("5f00::1")]
|
||||
#[case::unique_local_v6("fc00::1")]
|
||||
#[case::link_local_v6("fe80::1")]
|
||||
#[case::documentation_v6("2001:db8::1")]
|
||||
#[case::site_local_v6("fec0::1")]
|
||||
#[case::multicast_v6("ff02::1")]
|
||||
#[case::mapped_loopback_v6("::ffff:127.0.0.1")]
|
||||
#[case::mapped_azure_wire_server("::ffff:168.63.129.16")]
|
||||
fn blocks_non_public_addresses(#[case] address: &str) {
|
||||
assert!(is_blocked_ip(address.parse().expect("valid test address")));
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
#[case::public("8.8.8.8")]
|
||||
#[case::next_to_azure_wire_server("168.63.129.17")]
|
||||
#[case::next_to_carrier_grade_nat("100.128.0.1")]
|
||||
#[case::last_unicast_before_multicast("223.255.255.255")]
|
||||
#[case::public_v6("2606:4700::1111")]
|
||||
#[case::above_ietf_protocol_assignments_v6("2001:200::1")]
|
||||
#[case::mapped_public_v6("::ffff:8.8.8.8")]
|
||||
fn allows_a_public_address(#[case] address: &str) {
|
||||
assert!(!is_blocked_ip(
|
||||
address.parse().expect("valid public address")
|
||||
));
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BlockedIpFixture {
|
||||
rows: Vec<BlockedIpRow>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct BlockedIpRow {
|
||||
address: IpAddr,
|
||||
blocked: bool,
|
||||
}
|
||||
|
||||
/// Ranges Rust blocks where CPython's `ipaddress` reports global, with the reason. Each
|
||||
/// entry must still cover a diverging fixture row, so it is deleted once CPython catches up.
|
||||
const KNOWN_OVER_BLOCKED: &[(&str, &str)] = &[
|
||||
(
|
||||
"192.88.99.0/24",
|
||||
"deprecated 6to4 relay anycast, CPython never marked it non-global",
|
||||
),
|
||||
(
|
||||
"2001:1::1/128",
|
||||
"Port Control Protocol anycast, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
(
|
||||
"2001:1::2/128",
|
||||
"TURN anycast, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
(
|
||||
"2001:3::/32",
|
||||
"AMT relays, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
(
|
||||
"2001:4:112::/48",
|
||||
"AS112-v6, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
(
|
||||
"2001:20::/28",
|
||||
"ORCHIDv2, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
(
|
||||
"2001:30::/28",
|
||||
"Drone Remote ID, carved out of 2001::/23 by CPython",
|
||||
),
|
||||
("5f00::/16", "SRv6 SIDs, newer than CPython's table"),
|
||||
(
|
||||
"fec0::/10",
|
||||
"deprecated site-local, which CPython only exposes as `is_site_local`",
|
||||
),
|
||||
(
|
||||
"::/96",
|
||||
"deprecated IPv4-compatible addresses, Rust judges the embedded IPv4 address",
|
||||
),
|
||||
];
|
||||
|
||||
fn in_cidr(ip: IpAddr, cidr: &str) -> bool {
|
||||
let (network, prefix) = cidr.split_once('/').expect("cidr carries a prefix length");
|
||||
let prefix: u32 = prefix.parse().expect("prefix length is numeric");
|
||||
match (ip, network.parse().expect("cidr network parses")) {
|
||||
(IpAddr::V4(ip), IpAddr::V4(network)) => {
|
||||
in_network::<32>(u32::from(ip).into(), u32::from(network).into(), prefix)
|
||||
}
|
||||
(IpAddr::V6(ip), IpAddr::V6(network)) => {
|
||||
in_network::<128>(ip.into(), network.into(), prefix)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest::fixture]
|
||||
#[once]
|
||||
fn python_verdicts() -> BlockedIpFixture {
|
||||
serde_json::from_str(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/generated/blocked_ips.json"
|
||||
)))
|
||||
.expect("blocked_ips.json matches the fixture schema")
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
fn blocks_everything_the_python_policy_blocks(python_verdicts: &BlockedIpFixture) {
|
||||
let verdicts: Vec<(IpAddr, bool, bool)> = python_verdicts
|
||||
.rows
|
||||
.iter()
|
||||
.map(|row| (row.address, row.blocked, is_blocked_ip(row.address)))
|
||||
.collect();
|
||||
|
||||
let under_blocked: Vec<IpAddr> = verdicts
|
||||
.iter()
|
||||
.filter(|(_, python, rust)| *python && !*rust)
|
||||
.map(|(address, ..)| *address)
|
||||
.collect();
|
||||
assert!(
|
||||
under_blocked.is_empty(),
|
||||
"Python blocks but Rust allows: {under_blocked:?}"
|
||||
);
|
||||
|
||||
let over_blocked: Vec<IpAddr> = verdicts
|
||||
.iter()
|
||||
.filter(|(_, python, rust)| !*python && *rust)
|
||||
.map(|(address, ..)| *address)
|
||||
.collect();
|
||||
let unexplained: Vec<IpAddr> = over_blocked
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|address| {
|
||||
!KNOWN_OVER_BLOCKED
|
||||
.iter()
|
||||
.any(|(cidr, _)| in_cidr(*address, cidr))
|
||||
})
|
||||
.collect();
|
||||
assert!(
|
||||
unexplained.is_empty(),
|
||||
"Rust blocks but Python allows, missing from KNOWN_OVER_BLOCKED: {unexplained:?}"
|
||||
);
|
||||
|
||||
let stale: Vec<&str> = KNOWN_OVER_BLOCKED
|
||||
.iter()
|
||||
.filter(|(cidr, _)| !over_blocked.iter().any(|address| in_cidr(*address, cidr)))
|
||||
.map(|(cidr, _)| *cidr)
|
||||
.collect();
|
||||
assert!(
|
||||
stale.is_empty(),
|
||||
"KNOWN_OVER_BLOCKED entries that no longer diverge from Python: {stale:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetches_exact_limit_and_normalizes_content_type() {
|
||||
let (url, server) = serve(
|
||||
|
|
@ -582,6 +764,30 @@ mod tests {
|
|||
assert_eq!(media.bytes, b"ok");
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
#[case::six_to_four("2002:c000:204::1")]
|
||||
#[case::ietf_protocol_assignments_v4("192.0.0.9")]
|
||||
#[case::mapped_azure_wire_server("::ffff:168.63.129.16")]
|
||||
#[tokio::test]
|
||||
async fn host_resolving_to_a_newly_blocked_address_receives_no_request(
|
||||
#[case] resolved: IpAddr,
|
||||
) {
|
||||
let (url, server, address) = serve_named("blocked.test", vec![OK_RESPONSE]).await;
|
||||
let error = fetcher_resolving_blocked_hosts_to(
|
||||
address,
|
||||
HashSet::from(["blocked.test"]),
|
||||
resolved,
|
||||
UrlPolicy::default(),
|
||||
false,
|
||||
)
|
||||
.fetch(url, policy(10, 0))
|
||||
.await
|
||||
.expect_err("blocked destination is rejected");
|
||||
assert!(matches!(error, Error::BlockedUrl));
|
||||
assert!(!server.is_finished(), "server must not have seen a request");
|
||||
server.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocks_redirected_private_destination_before_second_request() {
|
||||
let (url, server, address) = serve_named(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue