Mask details in debug traces.

This commit is contained in:
2025-11-21 17:04:31 +01:00
parent cf5e6eee08
commit a4fcea1afe
2 changed files with 79 additions and 4 deletions

View File

@@ -1,7 +1,8 @@
use rust_decimal::Decimal;
use chrono::NaiveDate;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
#[derive(Clone, PartialEq)]
pub struct BankTransaction {
/// Source ID (GoCardless transactionId)
pub internal_id: String,
@@ -23,9 +24,83 @@ pub struct BankTransaction {
pub counterparty_iban: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
impl fmt::Debug for BankTransaction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BankTransaction")
.field("internal_id", &self.internal_id)
.field("date", &self.date)
.field("amount", &"[REDACTED]")
.field("currency", &self.currency)
.field("foreign_amount", &self.foreign_amount.as_ref().map(|_| "[REDACTED]"))
.field("foreign_currency", &self.foreign_currency)
.field("description", &"[REDACTED]")
.field("counterparty_name", &self.counterparty_name.as_ref().map(|_| "[REDACTED]"))
.field("counterparty_iban", &self.counterparty_iban.as_ref().map(|_| "[REDACTED]"))
.finish()
}
}
#[derive(Clone, PartialEq)]
pub struct Account {
pub id: String,
pub iban: String,
pub currency: String,
}
impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Account")
.field("id", &self.id)
.field("iban", &"[REDACTED]")
.field("currency", &self.currency)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal::Decimal;
#[test]
fn test_bank_transaction_debug_masks_sensitive_data() {
let tx = BankTransaction {
internal_id: "test-id".to_string(),
date: NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(),
amount: Decimal::new(12345, 2), // 123.45
currency: "EUR".to_string(),
foreign_amount: Some(Decimal::new(67890, 2)), // 678.90
foreign_currency: Some("USD".to_string()),
description: "Test transaction".to_string(),
counterparty_name: Some("Test Counterparty".to_string()),
counterparty_iban: Some("DE1234567890".to_string()),
};
let debug_str = format!("{:?}", tx);
assert!(debug_str.contains("internal_id"));
assert!(debug_str.contains("date"));
assert!(debug_str.contains("currency"));
assert!(debug_str.contains("foreign_currency"));
assert!(debug_str.contains("[REDACTED]"));
assert!(!debug_str.contains("123.45"));
assert!(!debug_str.contains("678.90"));
assert!(!debug_str.contains("Test transaction"));
assert!(!debug_str.contains("Test Counterparty"));
assert!(!debug_str.contains("DE1234567890"));
}
#[test]
fn test_account_debug_masks_iban() {
let account = Account {
id: "123".to_string(),
iban: "DE1234567890".to_string(),
currency: "EUR".to_string(),
};
let debug_str = format!("{:?}", account);
assert!(debug_str.contains("id"));
assert!(debug_str.contains("currency"));
assert!(debug_str.contains("[REDACTED]"));
assert!(!debug_str.contains("DE1234567890"));
}
}

View File

@@ -27,14 +27,14 @@ where
let end_date = cli_end_date.unwrap_or_else(|| Local::now().date_naive() - chrono::Duration::days(1));
for account in accounts {
let span = tracing::info_span!("sync_account", iban = %account.iban);
let span = tracing::info_span!("sync_account", account_id = %account.id);
let _enter = span.enter();
info!("Processing account...");
let dest_id_opt = destination.resolve_account_id(&account.iban).await?;
let Some(dest_id) = dest_id_opt else {
warn!("Account {} not found in destination. Skipping.", account.iban);
warn!("Account {} not found in destination. Skipping.", account.id);
continue;
};