use crate::hash::blake3_hash;
pub fn fingerprint(value: impl AsRef<[u8]>) -> String {
blake3_hash(value)
}
pub fn redacted(value: &str) -> String {
if value.chars().count() <= 8 {
return "****".to_string();
}
let prefix: String = value.chars().take(4).collect();
let suffix: String = value
.chars()
.rev()
.take(4)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
format!("{prefix}****{suffix}")
}
pub fn short_hash(hash: &str) -> String {
for prefix in ["blake3:", "sha256:"] {
if let Some(hex) = hash.strip_prefix(prefix) {
let head = &hex[..hex.len().min(8)];
return format!("{prefix}{head}...");
}
}
hash.to_string()
}
pub fn is_placeholder(value: &str) -> bool {
let lowered = value.trim_matches(['"', '\'']).to_ascii_lowercase();
lowered.is_empty()
|| lowered.contains("changeme")
|| lowered.contains("example")
|| lowered.contains("dummy")
|| lowered.contains("fake")
|| lowered.contains("test")
|| lowered.contains("your-key-here")
|| lowered == "xxx"
|| lowered == "xxxx"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn redacts_short_values_fully() {
assert_eq!(redacted("short"), "****");
}
#[test]
fn redacts_long_values_with_prefix_and_suffix() {
assert_eq!(redacted("long_demo_token_1234567890"), "long****7890");
}
#[test]
fn fingerprint_uses_blake3_prefix() {
let h = fingerprint("payload");
assert!(
h.starts_with("blake3:"),
"Phase 3 fingerprint must be BLAKE3 per ec ADR-0003, got {h:?}"
);
}
#[test]
fn fingerprint_is_deterministic() {
assert_eq!(fingerprint("same"), fingerprint("same"));
assert_ne!(fingerprint("same"), fingerprint("different"));
}
#[test]
fn short_hash_handles_blake3_prefix() {
let h = fingerprint("abc");
let short = short_hash(&h);
assert!(short.starts_with("blake3:"));
assert!(short.ends_with("..."));
assert!(short.len() < h.len());
}
#[test]
fn short_hash_accepts_legacy_sha256_prefix() {
let h = format!("sha256:{}", "a".repeat(64));
let short = short_hash(&h);
assert!(short.starts_with("sha256:"));
assert!(short.ends_with("..."));
}
#[test]
fn redaction_does_not_leak_whole_value() {
let secret = "tsafe_demo_token_1234567890";
assert!(!redacted(secret).contains(secret));
}
}