Skip to content

PII scrubber redacts bare 10-digit runs as NANP phones with no checksum at all — same corruption class as the credit-card Luhn FP (#154) #155

Description

@YellowSnnowmann

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions