forked from mirrors/kingfisher
updated in response to ossf scorecard
This commit is contained in:
parent
93cd6e940c
commit
993a76ded1
2 changed files with 38 additions and 3 deletions
|
|
@ -394,6 +394,8 @@ pub fn validate_response(
|
|||
|
||||
/// Returns `true` if the IP address is safe for outbound validation requests
|
||||
/// (i.e., it is a publicly routable address, not internal/reserved).
|
||||
///
|
||||
/// Covers all IANA special-purpose ranges from RFC 6890 and RFC 8190.
|
||||
pub fn is_ssrf_safe_ip(ip: &IpAddr) -> bool {
|
||||
if ip.is_loopback() || ip.is_unspecified() || ip.is_multicast() {
|
||||
return false;
|
||||
|
|
@ -423,10 +425,18 @@ pub fn is_ssrf_safe_ip(ip: &IpAddr) -> bool {
|
|||
if octets[0] == 100 && (64..=127).contains(&octets[1]) {
|
||||
return false;
|
||||
}
|
||||
// IANA Special Purpose (192.0.0.0/24, RFC 6890)
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 0 {
|
||||
return false;
|
||||
}
|
||||
// Documentation ranges (RFC 5737)
|
||||
if octets[0] == 192 && octets[1] == 0 && octets[2] == 2 {
|
||||
return false;
|
||||
}
|
||||
// 6to4 relay anycast (192.88.99.0/24, RFC 7526 — deprecated)
|
||||
if octets[0] == 192 && octets[1] == 88 && octets[2] == 99 {
|
||||
return false;
|
||||
}
|
||||
if octets[0] == 198 && octets[1] == 51 && octets[2] == 100 {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -468,6 +478,10 @@ pub fn is_ssrf_safe_ip(ip: &IpAddr) -> bool {
|
|||
if segments[0] & 0xffc0 == 0xfec0 {
|
||||
return false;
|
||||
}
|
||||
// Benchmarking (2001:2::/48, RFC 5180)
|
||||
if segments[0] == 0x2001 && segments[1] == 0x0002 && segments[2] == 0 {
|
||||
return false;
|
||||
}
|
||||
// Documentation (2001:db8::/32)
|
||||
if segments[0] == 0x2001 && segments[1] == 0x0db8 {
|
||||
return false;
|
||||
|
|
@ -679,6 +693,24 @@ mod tests {
|
|||
assert!(!is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0808, 0x0808))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_iana_special_purpose() {
|
||||
// 192.0.0.0/24 — IANA special-purpose (RFC 6890)
|
||||
assert!(!is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(192, 0, 0, 1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_6to4_relay_anycast() {
|
||||
// 192.88.99.0/24 — 6to4 relay anycast (RFC 7526, deprecated)
|
||||
assert!(!is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(192, 88, 99, 1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_ipv6_benchmarking() {
|
||||
// 2001:2::/48 — benchmarking (RFC 5180)
|
||||
assert!(!is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0x2001, 0x0002, 0, 0, 0, 0, 0, 1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_public_ipv4() {
|
||||
assert!(is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));
|
||||
|
|
|
|||
|
|
@ -130,9 +130,12 @@ pub struct ValidationClients {
|
|||
/// closes the hostname-redirect SSRF gap (e.g., a public URL that 302s to an
|
||||
/// attacker-controlled hostname resolving to `169.254.169.254`).
|
||||
///
|
||||
/// **Note:** Using blocking DNS in the redirect callback is acceptable because
|
||||
/// reqwest runs redirect callbacks on its connection thread, not the tokio
|
||||
/// event loop, and the DNS latency is negligible relative to the HTTP request.
|
||||
/// **Note:** reqwest runs redirect callbacks on Tokio worker threads, so the
|
||||
/// blocking DNS lookup here can briefly stall other async tasks on that thread.
|
||||
/// This is acceptable for a scanner workload because DNS is typically cached
|
||||
/// by the system resolver (<5ms), redirect hops are infrequent, and the
|
||||
/// alternative (disabling automatic redirects and following them manually with
|
||||
/// async DNS) would add significant complexity for minimal practical benefit.
|
||||
pub(crate) fn ssrf_safe_redirect_policy() -> reqwest::redirect::Policy {
|
||||
reqwest::redirect::Policy::custom(|attempt| {
|
||||
// Cap redirect depth (reqwest default is 10)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue