DE
Anmelden Veröffentlichen
contextetech /

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

v1
Zielmodell : Allgemein (alle Modelle) Englisch Lizenz: MIT Veröffentlicht am aktualisiert vor 1 Stunde 0 Nutzungen

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

Regeln

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.

Testfälle

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

Code

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

Noch keine Kommentare. Teile deine Erfahrung, sie hilft den Nächsten.