pub(crate) struct ConfidenceSignals {
pub has_literal_prefix: bool,
pub has_context_anchor: bool,
pub entropy: f64,
pub keyword_nearby: bool,
pub sensitive_file: bool,
pub match_length: usize,
pub has_companion: bool,
}
#[derive(serde::Deserialize)]
struct SensitivePathMarkers {
markers: Vec<String>,
}
fn parse_sensitive_path_markers(raw: &str) -> Result<Vec<String>, String> {
toml::from_str::<SensitivePathMarkers>(raw)
.map(|parsed| parsed.markers)
.map_err(|error| error.to_string())
}
static SENSITIVE_PATH_MARKERS: std::sync::LazyLock<Vec<String>> = std::sync::LazyLock::new(|| {
match parse_sensitive_path_markers(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/rules/sensitive-path-markers.toml"
))) {
Ok(markers) => markers,
Err(error) => panic!(
"rules/sensitive-path-markers.toml is invalid: {error}. \
Fix the bundled Tier-B sensitive-path marker list."
),
}
});
pub(crate) fn is_sensitive_path(path: &str) -> bool {
use std::sync::OnceLock;
static AC: OnceLock<Option<aho_corasick::AhoCorasick>> = OnceLock::new();
let ac = AC.get_or_init(|| {
match aho_corasick::AhoCorasickBuilder::new()
.ascii_case_insensitive(true)
.build(SENSITIVE_PATH_MARKERS.iter())
{
Ok(ac) => Some(ac),
Err(error) => {
eprintln!(
"keyhog: BUG, the sensitive-path marker list failed to \
build an Aho-Corasick automaton ({error}); an invalid marker \
is present in rules/sensitive-path-markers.toml. Treating every \
path as sensitive (fail toward recall) until the list is fixed."
);
None
}
}
});
ac.as_ref().is_none_or(|ac| ac.is_match(path))
}