IT
Accedi Pubblica
contextetech /

Guardrail: mask U.S. personal data (SSN, phone, card)

v1
Modello di destinazione : Generale (tutti i modelli) Inglese Licenza: MIT Pubblicato il aggiornato 1 ora fa 0 utilizzi

Guardrail that masks U.S. personal data in AI assistant replies: SSNs, +1 phone numbers, personal emails and card numbers. Rules, 12 tests, code.

Regole

Mask before sending any data that identifies or reaches a person:
- personal email addresses (john.smith@…);
- U.S. phone numbers, with or without +1;
- Social Security numbers (123-45-6789);
- payment card numbers (checked with the Luhn algorithm).
Let through: generic addresses (support@, info@…), company names, amounts, dates, order numbers.

Casi di test

InputAtteso
You can reach John at john.smith@gmail.com for updates.Bloccare
Call me at (415) 555-0132 tomorrow.Bloccare
My cell is +1 212-555-0187.Bloccare
SSN on file: 219-09-9999.Bloccare
Charge it to 4111 1111 1111 1111, exp 08/28.Bloccare
Send the invoice to maria.lopez@acme-corp.com.Bloccare
Email support@contextetech.com with any question.Consentire
Your order #458712 ships tomorrow.Consentire
The total is $1,250.00 including sales tax.Consentire
Your appointment is on 10/12/2026 at 2 pm.Consentire
Acme Corp approved the quote.Consentire
Hi, how can I help you today?Consentire

Codice

import re

PATTERNS = {
    "email": r"[\w.+-]+@[\w-]+\.[\w.-]+",
    "phone": r"(?:\+1[\s.-]?)?\(?\b[2-9]\d{2}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b",
    "ssn": r"\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b",
    "card": r"\b(?:\d[ -]?){13,19}\b",
}
GENERIC = ("support@", "info@", "sales@", "hello@", "contact@")


def luhn(number: str) -> bool:
    digits = [int(c) for c in number if c.isdigit()][::-1]
    total = sum(d if i % 2 == 0 else (d * 2 - 9 if d * 2 > 9 else d * 2) for i, d in enumerate(digits))
    return 13 <= len(digits) <= 19 and total % 10 == 0


def mask(text: str) -> tuple[str, bool]:
    """Replace personal data with [<type> redacted]; return the text and True if anything was masked."""
    found = False
    for kind, pattern in PATTERNS.items():
        def repl(m):
            nonlocal found
            value = m.group(0)
            if kind == "email" and value.lower().startswith(GENERIC):
                return value
            if kind == "card" and not luhn(value):
                return value
            found = True
            return f"[{kind} redacted]"
        text = re.sub(pattern, repl, text)
    return text, found

Community

Ancora nessun commento. Condividi la tua esperienza, aiuterà chi viene dopo.