Summary
#154 fixed the credit-card rule redacting bare Luhn-valid digit runs (~10% of arbitrary 13-19 digit runs), which was corrupting stored JSON envelopes downstream (tinyhumansai/opencompany#1201). The NANP phone rule has the same disease, worse: no checksum gates it at all.
static PHONE_NANP_RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\b(?:\+?1[\s.\-]?)?\(?([2-9]\d{2})\)?[\s.\-]?([2-9]\d{2})[\s.\-]?(\d{4})\b")
.expect("nanp phone")
});
A bare, separator-less 10-digit run matches whenever digit 1 and digit 4 are in [2-9] — roughly 64% of uniformly random 10-digit runs, and 100% of many real id schemes. Any such run inside machine text (a JSON payload stored through sanitize_text, an order id, a numeric account id) is rewritten to [REDACTED_PII_PHONE], silently. In a serialized envelope that means unparseable JSON — the exact corruption shape of opencompany#1201.
What does NOT hit it today, for the record: 13-digit epoch-millis (no internal \b), and 10/11-digit epoch-seconds (leading 17…/18… fails the [2-9] area-code rule). So nothing is known to be actively corrupting right now — this is the next latent instance of the class, not a live incident.
Suggested direction
Same split #154 applied to credit card, and this file already applies to Aadhaar: a separated/formatted NANP shape ((202) 555-0134, 202-555-0134, +1 202 555 0134) keeps matching as it does today; a bare 10-11 digit run needs corroboration — a phone keyword nearby (phone|call|tel|cell|mobile|text|sms|fax|#), or the +1/1- country-code prefix that is already optional in the pattern.
Note the strict boundary set (collect_strict_redactions) already excludes NANP for exactly this false-positive reason — the content path just never caught up, same as credit card hadn't.
E.164 (\+\d{7,15}) is fine: the literal + is its corroboration.
Found while fixing opencompany#1201; kept out of #154 to keep that change reviewable.
🤖 Generated with Claude Code
Summary
#154 fixed the credit-card rule redacting bare Luhn-valid digit runs (~10% of arbitrary 13-19 digit runs), which was corrupting stored JSON envelopes downstream (tinyhumansai/opencompany#1201). The NANP phone rule has the same disease, worse: no checksum gates it at all.
A bare, separator-less 10-digit run matches whenever digit 1 and digit 4 are in
[2-9]— roughly 64% of uniformly random 10-digit runs, and 100% of many real id schemes. Any such run inside machine text (a JSON payload stored throughsanitize_text, an order id, a numeric account id) is rewritten to[REDACTED_PII_PHONE], silently. In a serialized envelope that means unparseable JSON — the exact corruption shape of opencompany#1201.What does NOT hit it today, for the record: 13-digit epoch-millis (no internal
\b), and 10/11-digit epoch-seconds (leading17…/18…fails the[2-9]area-code rule). So nothing is known to be actively corrupting right now — this is the next latent instance of the class, not a live incident.Suggested direction
Same split #154 applied to credit card, and this file already applies to Aadhaar: a separated/formatted NANP shape (
(202) 555-0134,202-555-0134,+1 202 555 0134) keeps matching as it does today; a bare 10-11 digit run needs corroboration — a phone keyword nearby (phone|call|tel|cell|mobile|text|sms|fax|#), or the+1/1-country-code prefix that is already optional in the pattern.Note the strict boundary set (
collect_strict_redactions) already excludes NANP for exactly this false-positive reason — the content path just never caught up, same as credit card hadn't.E.164 (
\+\d{7,15}) is fine: the literal+is its corroboration.Found while fixing opencompany#1201; kept out of #154 to keep that change reviewable.
🤖 Generated with Claude Code