Skip to content

Context-cued bare SSNs evade both the redactor and adapter SSN detectors #3592

Description

@MohammadHaroonAbuomar

Both SSN detectors — credential_redactor.py ("US SSN", merged in #3531) and the adapter-side PII_PATTERNS entry (aligned in #3591) — require a separator between digit groups. That correctly stops bare nine-digit numbers (tracking/ABA/invoice numbers) from hard-blocking, but it also means an SSN written bare next to an explicit cue passes both gates:

  • SSN: 745102386
  • ssn=745102386
  • social security number 745102386

None of these match at either detector today (verified against both patterns), so tool output carrying a cued bare SSN is neither redacted at the gateway nor blocked by the adapters.

Suggested fix: add a context-cued branch to the shared pattern — a case-insensitive cue (ssn, social security) within a short window before an undelimited nine-digit run — applied identically in both files so the detectors stay in lockstep (divergence between them was nearly reintroduced twice; #3591 restored parity). Regression tests should pin the cued-bare forms as matches while keeping the bare-uncued FP corpus (from #3531/#3591) as non-matches.

Related: the Rego policy templates under examples/policy-templates/ deliberately keep the loose nine-digit form for detection-only scoring; if the cued branch lands, the templates could adopt it too.

Found while reviewing #3591; pre-existing on both sides, not introduced there.

Activity

  1. artificialvirus commented on Aug 3, 2026

    @artificialvirus
    Contributor

    Reproduced all three against both live patterns. Adapter and gateway, both no-match.

    I sketched the cued branch to see where the window lands, using
    (?i)(?:ssn|social\s+security(?:\s+number)?)\D{0,N}(?<![A-Za-z0-9])\d{9}(?![A-Za-z0-9]):

    • N=10 — all three cued forms match, and every false-positive probe I could construct stays clean.
    • N=20 — ssn_lookup_id=123456789, ssn service ticket 123456789, and no ssn on file; case 123456789 all match. The last one is worth noting: a negated mention still produces a hard deny.
      So the window looks like the whole design here, and it's sharp rather than gradual. Might be worth pinning the chosen N with both cued-positive and cued-negative cases so a later widening has to argue with a test.
      Separately: the two patterns are byte-identical today (credential_redactor.py:183, base.py:33), but nothing fails if they drift — no test compares them and neither module imports the other. Since this change edits both, a test asserting equality would make the next divergence fail CI instead of relying on review to catch it. Happy to send that as a small standalone PR ahead of the cued branch if useful.
      Are you taking this one? MohammadHaroonAbuomar
      If not, I'm glad to pick it up.
  2. added a commit that references this issue on Sep 17, 2026
    4decbb5
  3. added 2 commits that reference this issue on Sep 26, 2026
    87b54a6
    eda502d
  4. added a commit that references this issue on Sep 29, 2026
    bfc71da
  5. added a commit that references this issue on Oct 1, 2026
    a04fec1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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