pub(crate) use super::canonical::is_uuid_v4_shape as looks_like_entropy_uuid_shape;
use super::canonical::{
is_canonical_hex_digest_length, is_five_by_five_dash_shape, HASH_ALGO_INTEGRITY_LABELS,
};
pub(crate) const HIGH_ENTROPY_BASE64_CUTOFF: f64 = 4.8;
pub(crate) fn looks_like_entropy_canonical_non_secret_shape(value: &str) -> bool {
looks_like_entropy_uuid_shape(value)
|| looks_like_entropy_canonical_hex_digest(value)
|| looks_like_entropy_integrity_digest(value)
|| looks_like_entropy_upper_license_serial(value)
}
pub(crate) fn looks_like_entropy_canonical_hex_digest(value: &str) -> bool {
is_canonical_hex_digest_length(value.len()) && value.bytes().all(|b| b.is_ascii_hexdigit())
}
fn looks_like_entropy_integrity_digest(value: &str) -> bool {
HASH_ALGO_INTEGRITY_LABELS.iter().any(|&prefix| {
value.strip_prefix(prefix).is_some_and(|body| {
!body.is_empty() && crate::decode::standard_base64_shape(body).is_some()
})
})
}
fn looks_like_entropy_upper_license_serial(value: &str) -> bool {
is_five_by_five_dash_shape(value, |b| b.is_ascii_uppercase() || b.is_ascii_digit())
}
#[cfg(feature = "entropy")]
pub(crate) fn looks_like_entropy_random_base64_blob_decoy(value: &str) -> bool {
crate::decode_structure::is_byte_distribution_base64_blob(value, 50, 300)
}
pub(crate) fn looks_like_generic_random_base64_blob_decoy(value: &str, entropy: f64) -> bool {
if entropy >= HIGH_ENTROPY_BASE64_CUTOFF {
return false;
}
crate::decode_structure::is_byte_distribution_base64_blob(value, 40, 300)
}
pub(crate) fn generic_base64_candidate_is_ambiguous(value: &str, entropy: f64) -> bool {
const MIN_DISTINCT_ALNUM: u32 = 32;
if entropy < HIGH_ENTROPY_BASE64_CUTOFF {
return false;
}
let Some(shape) = crate::decode::standard_base64_shape(value) else {
return false;
};
shape.distinct_alnum >= MIN_DISTINCT_ALNUM
}
#[cfg(test)]
#[path = "../../../tests/unit/suppression_shape_assignment.rs"]
mod tests;