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.
Règles
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.
Cas de test
| Entrée | Attendu |
|---|---|
| Può scrivere a mario.rossi@gmail.com per aggiornamenti. | Bloquer |
| Il mio cellulare è 347 123 4567. | Bloquer |
| Mi chiami al +39 06 1234 5678 domani. | Bloquer |
| Bonifico su IT60 X054 2811 1010 0000 0123 456. | Bloquer |
| Codice fiscale: RSSMRA85T10A562S. | Bloquer |
| Fattura da inviare a giulia.bianchi@azienda.it. | Bloquer |
| Scriva ad assistenza@contextetech.com per qualsiasi domanda. | Laisser passer |
| Il suo ordine n. 458712 parte domani. | Laisser passer |
| L'importo è di 1.250,00 € IVA inclusa. | Laisser passer |
| Appuntamento il 12/10/2026 alle 14. | Laisser passer |
| L'azienda Costruzioni Esposito S.r.l. ha accettato il preventivo. | Laisser passer |
| Buongiorno, come posso aiutarla? | Laisser passer |
Code
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, trovatoCoût estimé
Tokens d'entrée par appel : ≈ 1 007
| Modèle | Par appel | Pour 1 000 appels |
|---|---|---|
| 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 € |
| Modèle open source hébergé chez soi (Ollama, vLLM) | 0 € d'API (coût du serveur seulement) | |
Coût d'entrée seulement, sans la réponse du modèle. Prix publics des fournisseurs (Mistral AI, DeepSeek, OpenAI, Anthropic), tarif standard en dollars converti en euros au taux BCE du 29 septembre 2026. Estimation : 1 token ≈ 3,6 caractères.
Commun aux 3 régions
Contenu créé par l'auteur, sans source extérieure.
Usage commercial autorisé · citer l'auteur · modifications permises.
Texte ou données seuls : aucun accès aux fichiers, au réseau ni aux commandes.
Hébergé en France (Scaleway, Paris). Aucun transfert hors UE.
Union européenne · 5/5
Fictives : noms, adresses et numéros inventés, aucune personne réelle.
Pas de données d'entraînement : pas de résumé à fournir.
États-Unis · 5/5
Fictives : noms, adresses et numéros inventés, aucune personne réelle.
Pas de données d'entraînement : pas de documentation à publier.
Chine · 5/5
Fictives : noms, adresses et numéros inventés, aucune personne réelle.
Pas de données d'entraînement ; les contenus générés par IA diffusés en Chine doivent être marqués (2025).
Synthèse indicative au 30/09/2026, sans valeur de certification juridique. Méthode et sources →
Statistiques
J'aime : 0 · Commentaires : 0
Cliquez sur un compteur pour l’afficher ou le masquer, survolez le graphique pour le détail. Une vue par visiteur et par jour, robots exclus ; décompte commencé le 30 septembre 2026.
- Identifiant
- contextetech--oscurare-dati-personali-gdpr
- Type
- Garde-fous
- Fichier
- oscurare-dati-personali-gdpr.guardrail.json
- Format
- JSON (application/json)
- Taille
- 2 029 octets (2 Ko)
- Encodage
- UTF-8
- Tokens estimés
- ≈ 561
- Langue
- Italien
- Licence
- MIT
- Usage commercial
- Autorisé
- Données personnelles
- Fictives (inventées)
- Version
- 1.0
- Publié le
- 3 octobre 2026 à 05:20
- Mis à jour le
- 3 octobre 2026 à 05:20
- Stockage
- en base
- Utilisations
- 0
- J'aime
- 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"
}
]
}Vérifier un texte (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
Communauté
Pas encore de commentaire. Partagez votre retour, il aidera les suivants.