Decode Base64 blobs and scan their contents for secrets while skipping short strings for performance. This has a small performance impact and can be disabled with --no-base64

This commit is contained in:
Mick Grove 2025-08-30 19:40:22 -07:00
commit aa2c3ba0cc
5 changed files with 27 additions and 15 deletions

View file

@ -255,7 +255,9 @@ async fn async_main(args: CommandLineArgs) -> Result<()> {
}
},
},
Command::SelfUpdate => anyhow::bail!("SelfUpdate command should not reach this branch"),
Command::SelfUpdate => {
anyhow::bail!("SelfUpdate command should not reach this branch")
}
}
if let Some(msg) = update_msg {
info!("{msg}");

View file

@ -430,7 +430,7 @@ impl<'a> Matcher<'a> {
}
}
if !no_base64 {
if !no_base64 {
// If the blob contains standalone Base64 blobs, decode and scan them as well
const MAX_B64_DEPTH: usize = 2; // decode at most two levels deep
let mut b64_stack: Vec<(DecodedData, usize)> =

View file

@ -107,10 +107,7 @@ pub async fn validate_mongodb(uri: &str) -> Result<(bool, String)> {
// ---- refuse localhost/loopback/UDS outright
if uri_targets_localhost(uri) {
return Ok((
false,
"Refusing to validate localhost/loopback MongoDB URIs.".to_string(),
));
return Ok((false, "Refusing to validate localhost/loopback MongoDB URIs.".to_string()));
}
let is_srv = uri.starts_with("mongodb+srv://");
@ -164,4 +161,4 @@ pub fn generate_mongodb_cache_key(mongodb_uri: &str) -> String {
let mut hasher = Sha1::new();
hasher.update(mongodb_uri.as_bytes());
format!("MongoDB:{:x}", hasher.finalize())
}
}

View file

@ -6,7 +6,11 @@ use rustls::{client::ClientConfig, RootCertStore};
use rustls_native_certs::{load_native_certs, CertificateResult};
use sha1::{Digest, Sha1};
use tokio::time::{error::Elapsed, timeout};
use tokio_postgres::{config::{Host, SslMode}, tls::NoTls, Config, Error};
use tokio_postgres::{
config::{Host, SslMode},
tls::NoTls,
Config, Error,
};
use tokio_postgres_rustls::MakeRustlsConnect;
use tracing::debug;
@ -59,10 +63,12 @@ fn is_local_tcp_host(s: &str) -> bool {
// Direct IPs
if let Ok(ip) = host.parse::<std::net::IpAddr>() {
return match ip {
std::net::IpAddr::V4(v4) =>
v4.is_loopback() || v4.is_unspecified() || v4.is_link_local(),
std::net::IpAddr::V6(v6) =>
v6.is_loopback() || v6.is_unspecified() || v6.is_unicast_link_local(),
std::net::IpAddr::V4(v4) => {
v4.is_loopback() || v4.is_unspecified() || v4.is_link_local()
}
std::net::IpAddr::V6(v6) => {
v6.is_loopback() || v6.is_unspecified() || v6.is_unicast_link_local()
}
};
}
@ -74,7 +80,6 @@ fn is_local_tcp_host(s: &str) -> bool {
|| lower.starts_with("localhost6.")
}
async fn check_postgres_db_connection(
mut cfg: Config,
original_mode: SslMode,
@ -201,7 +206,15 @@ mod tests {
#[test]
fn detects_local_hosts() {
for h in ["localhost", "LOCALHOST", "localhost.localdomain", "localhost6", "127.0.0.1", "[::1]", "::"] {
for h in [
"localhost",
"LOCALHOST",
"localhost.localdomain",
"localhost6",
"127.0.0.1",
"[::1]",
"::",
] {
assert!(is_local_tcp_host(h), "should treat {h} as local");
}
for h in ["db.example.com", "10.0.0.1"] {

View file

@ -58,4 +58,4 @@ fn skips_base64_when_disabled() -> anyhow::Result<()> {
dir.close()?;
Ok(())
}
}