use std::sync::Once;
pub const UNSAFE_FULL_LOGGING_WARNING: &str =
"unsafe full logging may expose raw financial values and must not be enabled by default";
pub fn install_redacted_panic_hook() {
static INSTALL: Once = Once::new();
INSTALL.call_once(|| std::panic::set_hook(Box::new(|_| {})));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SensitiveKind {
Iban,
Account,
Bic,
Name,
Address,
TransactionReference,
Remittance,
MessageXml,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RedactionPolicy {
allow_full_values: bool,
}
impl RedactionPolicy {
pub const fn allows_full_values(self) -> bool {
self.allow_full_values
}
pub fn redact(self, kind: SensitiveKind, value: &str) -> String {
if self.allow_full_values {
return value.to_owned();
}
match kind {
SensitiveKind::Iban | SensitiveKind::Account | SensitiveKind::Bic => {
mask_identifier(value)
}
SensitiveKind::Name
| SensitiveKind::Address
| SensitiveKind::TransactionReference
| SensitiveKind::Remittance
| SensitiveKind::MessageXml => "[REDACTED]".to_owned(),
}
}
#[cfg(feature = "unsafe-full-logging")]
pub const fn with_unsafe_full_logging(mut self, _: UnsafeLoggingPermit) -> Self {
self.allow_full_values = true;
self
}
}
fn mask_identifier(value: &str) -> String {
let characters: Vec<char> = value.chars().collect();
if characters.len() <= 10 {
return "[REDACTED]".to_owned();
}
let prefix: String = characters[..4].iter().collect();
let suffix: String = characters[characters.len() - 6..].iter().collect();
format!("{prefix}{}{suffix}", "*".repeat(characters.len() - 10))
}
#[cfg(feature = "unsafe-full-logging")]
#[derive(Debug, Clone, Copy)]
pub struct UnsafeLoggingPermit(());
#[cfg(feature = "unsafe-full-logging")]
impl UnsafeLoggingPermit {
pub const fn warning(self) -> &'static str {
UNSAFE_FULL_LOGGING_WARNING
}
pub const unsafe fn acknowledge_risk() -> Self {
Self(())
}
}