Salvaguardia GDPR: oscurare codice fiscale, telefono e IBAN
Salvaguardia GDPR che oscura codice fiscale (con controllo), telefoni +39, IBAN ed email personali nelle risposte di un assistente IA. 12 test.
Regole
Oscurare prima dell'invio qualsiasi dato che identifichi o renda contattabile una persona: - indirizzi email personali (mario.rossi@…); - numeri di telefono italiani, con o senza +39; - IBAN italiani; - codici fiscali (si verifica il carattere di controllo). Lasciar passare: indirizzi generici (info@, assistenza@…), nomi di aziende, importi, date, numeri d'ordine.
Casi di test
| Input | Atteso |
|---|---|
| Può scrivere a mario.rossi@gmail.com per aggiornamenti. | Bloccare |
| Il mio cellulare è 347 123 4567. | Bloccare |
| Mi chiami al +39 06 1234 5678 domani. | Bloccare |
| Bonifico su IT60 X054 2811 1010 0000 0123 456. | Bloccare |
| Codice fiscale: RSSMRA85T10A562S. | Bloccare |
| Fattura da inviare a giulia.bianchi@azienda.it. | Bloccare |
| Scriva ad assistenza@contextetech.com per qualsiasi domanda. | Consentire |
| Il suo ordine n. 458712 parte domani. | Consentire |
| L'importo è di 1.250,00 € IVA inclusa. | Consentire |
| Appuntamento il 12/10/2026 alle 14. | Consentire |
| L'azienda Costruzioni Esposito S.r.l. ha accettato il preventivo. | Consentire |
| Buongiorno, come posso aiutarla? | Consentire |
Codice
import re
SCHEMI = {
"email": r"[\w.+-]+@[\w-]+\.[\w.-]+",
"telefono": r"(?:\+39[\s.-]?)?\b(?:3\d{2}|0\d{1,3})[\s.-]?\d{3,4}[\s.-]?\d{3,4}\b",
"iban": r"\bIT\d{2}\s?[A-Z]\d{3}(?:\s?[\dA-Z]{4}){5}\b",
"codice_fiscale": r"\b[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]\b",
}
GENERICI = ("info@", "contatti@", "assistenza@", "supporto@", "ciao@")
DISPARI = dict(zip("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
[1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20, 11, 3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23]))
def codice_fiscale_valido(cf: str) -> bool:
"""Il sedicesimo carattere è il carattere di controllo calcolato sui primi quindici."""
cf = cf.upper()
somma = sum(DISPARI[c] if i % 2 == 0 else (int(c) if c.isdigit() else ord(c) - 65) for i, c in enumerate(cf[:15]))
return chr(65 + somma % 26) == cf[15]
def mascherare(testo: str) -> tuple[str, bool]:
"""Sostituisce i dati personali con [<tipo> oscurato]; restituisce il testo e True se qualcosa è stato oscurato."""
trovato = False
for tipo, schema in SCHEMI.items():
def sostituire(m):
nonlocal trovato
valore = m.group(0)
if tipo == "email" and valore.lower().startswith(GENERICI):
return valore
if tipo == "codice_fiscale" and not codice_fiscale_valido(valore):
return valore
trovato = True
return f"[{tipo} oscurato]"
testo = re.sub(schema, sostituire, testo)
return testo, trovatoCosto stimato
Token di input per chiamata : ≈ 1007
| Modello | Per chiamata | Per 1.000 chiamate |
|---|---|---|
| Mistral Large · Mistral AI | 0,00044 € | 0,44 € |
| DeepSeek Flash · DeepSeek | 0,00027 € | 0,27 € |
| DeepSeek V4 Pro · DeepSeek | 0,00117 € | 1,17 € |
| GPT-6 Luna · OpenAI | 0,00009 € | 0,09 € |
| GPT-6 Sol · OpenAI | 0,00177 € | 1,77 € |
| Claude Sonnet 5.5 · Anthropic | 0,00177 € | 1,77 € |
| Claude Opus 5.5 · Anthropic | 0,00355 € | 3,55 € |
| Modello open source self-hosted (Ollama, vLLM) | 0 € di API (solo costo del server) | |
Solo costo di input, esclusa la risposta del modello. Prezzi pubblici standard dei fornitori (Mistral AI, DeepSeek, OpenAI, Anthropic) convertiti da USD in euro al cambio BCE del 29 settembre 2026. Stima: 1 token ≈ 3,6 caratteri.
Common to all 3 regions
Created by the author, no outside source.
Commercial use allowed · credit the author · changes allowed.
Text or data only: no access to files, network or commands.
Hosted in France (Scaleway, Paris). No transfer outside the EU.
European Union · 5/5
Fictional: invented names, addresses and numbers, no real person.
No training data: no summary required.
United States · 5/5
Fictional: invented names, addresses and numbers, no real person.
No training data: no documentation to publish.
China · 5/5
Fictional: invented names, addresses and numbers, no real person.
No training data; AI-generated content published in China must be labelled (2025).
Indicative summary as of 30/09/2026, not a legal certification. Method and sources →
Statistiche
Mi piace : 0 · Commenti : 0
Clicca su un contatore per mostrarlo o nasconderlo; passa sul grafico per i dettagli. Una visualizzazione per visitatore al giorno, bot esclusi; conteggio iniziato il 30 settembre 2026.
- Identificativo
- contextetech--oscurare-dati-personali-gdpr
- Tipo
- Salvaguardie
- File
- oscurare-dati-personali-gdpr.guardrail.json
- Formato
- JSON (application/json)
- Dimensione
- 2029 byte (2 KB)
- Codifica
- UTF-8
- Token stimati
- ≈ 561
- Lingua
- Italiano
- Licenza
- MIT
- Uso commerciale
- Consentito
- Dati personali
- Fittizi (inventati)
- Versione
- 1.0
- Pubblicato il
- 3 ottobre 2026 alle ore 05:20
- Aggiornato il
- 3 ottobre 2026 alle ore 05:20
- Archiviazione
- nel database
- Utilizzi
- 0
- Mi piace
- 0
oscurare-dati-personali-gdpr.guardrail.json
{
"format": "contextetech/guardrail/v1",
"id": "contextetech/oscurare-dati-personali-gdpr",
"description": "Salvaguardia GDPR che oscura codice fiscale (con controllo), telefoni +39, IBAN ed email personali nelle risposte di un assistente IA. 12 test.",
"tags": [
"gdpr",
"dati-personali",
"sicurezza"
],
"license": "MIT",
"language": "it",
"type": "pii",
"action": "mask",
"rules": "Oscurare prima dell'invio qualsiasi dato che identifichi o renda contattabile una persona:\n- indirizzi email personali (mario.rossi@…);\n- numeri di telefono italiani, con o senza +39;\n- IBAN italiani;\n- codici fiscali (si verifica il carattere di controllo).\nLasciar passare: indirizzi generici (info@, assistenza@…), nomi di aziende, importi, date, numeri d'ordine.",
"cases": [
{
"input": "Può scrivere a mario.rossi@gmail.com per aggiornamenti.",
"expected": "block"
},
{
"input": "Il mio cellulare è 347 123 4567.",
"expected": "block"
},
{
"input": "Mi chiami al +39 06 1234 5678 domani.",
"expected": "block"
},
{
"input": "Bonifico su IT60 X054 2811 1010 0000 0123 456.",
"expected": "block"
},
{
"input": "Codice fiscale: RSSMRA85T10A562S.",
"expected": "block"
},
{
"input": "Fattura da inviare a giulia.bianchi@azienda.it.",
"expected": "block"
},
{
"input": "Scriva ad assistenza@contextetech.com per qualsiasi domanda.",
"expected": "allow"
},
{
"input": "Il suo ordine n. 458712 parte domani.",
"expected": "allow"
},
{
"input": "L'importo è di 1.250,00 € IVA inclusa.",
"expected": "allow"
},
{
"input": "Appuntamento il 12/10/2026 alle 14.",
"expected": "allow"
},
{
"input": "L'azienda Costruzioni Esposito S.r.l. ha accettato il preventivo.",
"expected": "allow"
},
{
"input": "Buongiorno, come posso aiutarla?",
"expected": "allow"
}
]
}Verificare un testo (Python)
import re
SCHEMI = {
"email": r"[\w.+-]+@[\w-]+\.[\w.-]+",
"telefono": r"(?:\+39[\s.-]?)?\b(?:3\d{2}|0\d{1,3})[\s.-]?\d{3,4}[\s.-]?\d{3,4}\b",
"iban": r"\bIT\d{2}\s?[A-Z]\d{3}(?:\s?[\dA-Z]{4}){5}\b",
"codice_fiscale": r"\b[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]\b",
}
GENERICI = ("info@", "contatti@", "assistenza@", "supporto@", "ciao@")
DISPARI = dict(zip("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
[1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 1, 0, 5, 7, 9, 13, 15, 17, 19, 21, 2, 4, 18, 20, 11, 3, 6, 8, 12, 14, 16, 10, 22, 25, 24, 23]))
def codice_fiscale_valido(cf: str) -> bool:
"""Il sedicesimo carattere è il carattere di controllo calcolato sui primi quindici."""
cf = cf.upper()
somma = sum(DISPARI[c] if i % 2 == 0 else (int(c) if c.isdigit() else ord(c) - 65) for i, c in enumerate(cf[:15]))
return chr(65 + somma % 26) == cf[15]
def mascherare(testo: str) -> tuple[str, bool]:
"""Sostituisce i dati personali con [<tipo> oscurato]; restituisce il testo e True se qualcosa è stato oscurato."""
trovato = False
for tipo, schema in SCHEMI.items():
def sostituire(m):
nonlocal trovato
valore = m.group(0)
if tipo == "email" and valore.lower().startswith(GENERICI):
return valore
if tipo == "codice_fiscale" and not codice_fiscale_valido(valore):
return valore
trovato = True
return f"[{tipo} oscurato]"
testo = re.sub(schema, sostituire, testo)
return testo, trovato
Community
Ancora nessun commento. Condividi la tua esperienza, aiuterà chi viene dopo.