From a0e713d752488246b089684f3f89e1508e4bf0fa Mon Sep 17 00:00:00 2001 From: flemming-it Date: Tue, 7 Jul 2026 20:22:12 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20redact=20grouped=20German=20IBAN=20(DE89?= =?UTF-8?q?=203704=20=E2=80=A6)=20as=20one=20IBAN,=20not=20partial+phone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: flemming-it --- src/lib.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8f77bcf..61bec1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,11 +227,15 @@ fn anonymize(text: &str, custom_raw: &str) -> Result<(String, Vec), M "IBAN", // Match the structural shape, surrounded by word // boundaries so we don't slice into longer alnum - // strings. + // strings. The BBAN body allows an OPTIONAL single space + // before each character so the standard grouped spelling + // (`DE89 3704 0044 0532 0130 00`) is redacted, not just + // the contiguous form — otherwise the leading group leaks + // and the phone rule mislabels the rest. Regex::new(r"(?x) \b [A-Z]{2}\d{2} - [A-Z0-9]{11,30} + (?:\s?[A-Z0-9]){11,30} \b ").map_err(re_err)?, ), @@ -434,6 +438,23 @@ mod tests { assert_eq!(reds.iter().filter(|r| r.kind == "BIC").count(), 1); } + #[test] + fn redacts_grouped_iban_without_phone_misclassification() { + // The standard German spelling groups the IBAN in 4-char + // blocks with spaces. It must redact as a single IBAN — not + // leak the leading group while the phone rule eats the rest. + let (out, reds) = + anonymize("Zahlung an DE89 3704 0044 0532 0130 00 heute", "").unwrap(); + assert!( + out.contains("\u{27E6}IBAN_1\u{27E7}"), + "grouped IBAN not redacted: {out}" + ); + assert!(!out.contains("PHONE"), "grouped IBAN mislabeled as phone: {out}"); + assert!(!out.contains("DE89"), "leading IBAN group leaked: {out}"); + assert_eq!(reds.iter().filter(|r| r.kind == "IBAN").count(), 1); + assert_eq!(reds.iter().filter(|r| r.kind == "PHONE").count(), 0); + } + #[test] fn redacts_phone_numbers() { let cases = [