updated in response to ossf scorecard

This commit is contained in:
Mick Grove 2026-03-27 22:43:50 -07:00
commit 93cd6e940c
3 changed files with 29 additions and 13 deletions

View file

@ -528,6 +528,13 @@ pub async fn check_url_resolvable(
Ok(())
}
/// Backwards-compatible wrapper: checks URL resolvability with SSRF protection
/// enabled (i.e., `allow_internal_ips = false`).
#[deprecated(since = "0.1.0", note = "use check_url_resolvable(url, allow_internal_ips) instead")]
pub async fn check_url_resolvable_safe(url: &Url) -> Result<(), Box<dyn std::error::Error>> {
check_url_resolvable(url, false).await
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -59,9 +59,11 @@ pub use utils::{find_closest_variable, process_captures};
pub use validation_body::{as_str, clone_as_string, from_string, ValidationResponseBody};
#[cfg(feature = "validation-http")]
#[allow(deprecated)]
pub use http_validation::{
build_request_builder, check_url_resolvable, generate_http_cache_key_parts, is_ssrf_safe_ip,
parse_http_method, process_headers, retry_multipart_request, retry_request, validate_response,
build_request_builder, check_url_resolvable, check_url_resolvable_safe,
generate_http_cache_key_parts, is_ssrf_safe_ip, parse_http_method, process_headers,
retry_multipart_request, retry_request, validate_response,
};
#[cfg(feature = "validation-aws")]

View file

@ -153,20 +153,27 @@ pub(crate) fn ssrf_safe_redirect_policy() -> reqwest::redirect::Policy {
} else {
// Hostname: resolve synchronously and check all resolved IPs.
let port = url.port().unwrap_or(if url.scheme() == "https" { 443 } else { 80 });
if let Ok(addrs) = std::net::ToSocketAddrs::to_socket_addrs(&(host, port as u16)) {
for addr in addrs {
if !kingfisher_scanner::validation::is_ssrf_safe_ip(&addr.ip()) {
return attempt.error(format!(
"SSRF protection: redirect to '{}' resolves to non-public IP {} — blocked",
host,
addr.ip()
));
match std::net::ToSocketAddrs::to_socket_addrs(&(host, port as u16)) {
Ok(addrs) => {
for addr in addrs {
if !kingfisher_scanner::validation::is_ssrf_safe_ip(&addr.ip()) {
return attempt.error(format!(
"SSRF protection: redirect to '{}' resolves to non-public IP {} — blocked",
host,
addr.ip()
));
}
}
}
Err(e) => {
// Fail closed: if we cannot resolve the hostname, we
// cannot guarantee the redirect target is SSRF-safe.
return attempt.error(format!(
"SSRF protection: cannot resolve redirect host '{}' ({}) — blocked",
host, e
));
}
}
// If DNS resolution fails, allow the redirect — reqwest will
// fail on connect anyway, and we don't want to silently block
// valid hostnames that are transiently unresolvable.
}
}
attempt.follow()