updated in response to ossf scorecard

This commit is contained in:
Mick Grove 2026-03-27 21:08:39 -07:00
commit 051e4ffdd2
2 changed files with 28 additions and 1 deletions

View file

@ -3,7 +3,7 @@
All notable changes to this project will be documented in this file.
## [v1.91.0]
- Added SSRF protection for credential validation: outbound HTTP requests now block connections to loopback, private, link-local, and other non-public IP addresses. Redirect targets are also validated. Use `--allow-internal-ips` to opt out when scanning internal infrastructure.
- Added SSRF protection for credential validation: outbound HTTP requests now block connections to loopback, private, link-local, and other non-public IP addresses. Redirects to non-public IP-literal addresses are also blocked. Use `--allow-internal-ips` to opt out when scanning internal infrastructure.
- Consolidated JWT SSRF checks to use the shared `is_ssrf_safe_ip` function, covering additional reserved ranges (CGNAT, documentation, benchmarking, IPv6 unique-local).
- Removed `ipnet` dependency from `kingfisher-scanner` (no longer needed).
- Remediated current RustSec vulnerability findings by upgrading core dependencies including `gix`, `mysql_async`, `axum`, `indicatif`, `quick-xml`, and `console`.

View file

@ -401,6 +401,10 @@ pub fn is_ssrf_safe_ip(ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(v4) => {
let octets = v4.octets();
// 0.0.0.0/8 — "This host on this network" (RFC 1122); not routable
if octets[0] == 0 {
return false;
}
// Private ranges (RFC 1918)
if octets[0] == 10 {
return false;
@ -446,6 +450,12 @@ pub fn is_ssrf_safe_ip(ip: &IpAddr) -> bool {
return is_ssrf_safe_ip(&IpAddr::V4(mapped));
}
let segments = v6.segments();
// IPv4-compatible IPv6 addresses (::/96, e.g., ::127.0.0.1) are
// deprecated (RFC 4291 §2.5.5.1) and can bypass IPv4-only checks.
// Reject the entire ::/96 range.
if segments[..6].iter().all(|&s| s == 0) {
return false;
}
// Unique local (fc00::/7)
if segments[0] & 0xfe00 == 0xfc00 {
return false;
@ -507,6 +517,13 @@ mod tests {
assert!(!is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::UNSPECIFIED)));
}
#[test]
fn rejects_ipv4_this_network() {
// 0.0.0.0/8 — "This host on this network" (RFC 1122)
assert!(!is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 1))));
assert!(!is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(0, 255, 255, 255))));
}
#[test]
fn rejects_ipv4_private_rfc1918() {
// 10.0.0.0/8
@ -598,6 +615,16 @@ mod tests {
assert!(is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x0808, 0x0808))));
}
#[test]
fn rejects_ipv4_compatible_ipv6() {
// ::127.0.0.1 — deprecated IPv4-compatible IPv6 (loopback)
assert!(!is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7f00, 0x0001))));
// ::10.0.0.1 — deprecated IPv4-compatible IPv6 (private)
assert!(!is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0a00, 0x0001))));
// ::8.8.8.8 — even public IPv4 in ::/96 is rejected (deprecated range)
assert!(!is_ssrf_safe_ip(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0808, 0x0808))));
}
#[test]
fn accepts_public_ipv4() {
assert!(is_ssrf_safe_ip(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))));