use anyhow::{Context, Result};
pub fn guard_credd_transport(url: &str) -> Result<()> {
let parsed = reqwest::Url::parse(url).context("invalid CREDD_URL")?;
if parsed.scheme() == "https" {
return Ok(());
}
let host = parsed.host_str().unwrap_or("");
let host_ip = host.trim_start_matches('[').trim_end_matches(']');
let is_loopback = host.eq_ignore_ascii_case("localhost")
|| host_ip
.parse::<std::net::IpAddr>()
.map(|ip| ip.is_loopback())
.unwrap_or(false);
if is_loopback {
return Ok(());
}
anyhow::bail!(
"refusing to send credentials over plaintext http to non-loopback host '{host}'; \
use an https CREDD_URL or a loopback address"
)
}
#[cfg(test)]
mod tests {
use super::guard_credd_transport;
#[test]
fn allows_loopback_http_and_https() {
assert!(guard_credd_transport("http://127.0.0.1:4400").is_ok());
assert!(guard_credd_transport("http://localhost:4400").is_ok());
assert!(guard_credd_transport("http://[::1]:4400").is_ok());
assert!(guard_credd_transport("https://credd.example.com").is_ok());
}
#[test]
fn rejects_plaintext_non_loopback() {
assert!(guard_credd_transport("http://credd.example.com").is_err());
assert!(guard_credd_transport("http://10.0.0.5:4400").is_err());
}
}