Salvaguarda RGPD: ocultar DNI, teléfonos e IBAN
Salvaguarda RGPD que oculta DNI y NIE (letra comprobada), teléfonos +34, IBAN y correos personales en las respuestas de un asistente de IA. 12 pruebas.
Règles
Ocultar antes del envío cualquier dato que identifique o permita contactar con una persona: - correos electrónicos personales (ana.garcia@…); - teléfonos españoles, con o sin +34; - IBAN españoles; - DNI y NIE (se comprueba la letra de control). Dejar pasar: direcciones genéricas (info@, soporte@…), nombres de empresas, importes, fechas, números de pedido.
Cas de test
| Entrée | Attendu |
|---|---|
| Puede escribir a ana.garcia@gmail.com para el seguimiento. | Bloquer |
| Mi móvil es el 612 345 678. | Bloquer |
| Llámeme al +34 912 345 678 mañana. | Bloquer |
| Transferencia a ES91 2100 0418 4502 0005 1332. | Bloquer |
| Mi DNI es 12345678Z. | Bloquer |
| NIE del cliente: X1234567L. | Bloquer |
| Escriba a soporte@contextetech.com para cualquier duda. | Laisser passer |
| Su pedido n.º 458712 sale mañana. | Laisser passer |
| El importe es de 1.250,00 € con IVA. | Laisser passer |
| La cita es el 12/10/2026 a las 14:00. | Laisser passer |
| La empresa Construcciones López S.L. aprobó el presupuesto. | Laisser passer |
| Hola, ¿en qué puedo ayudarle? | Laisser passer |
Code
import re
LETRAS = "TRWAGMYFPDXBNJZSQVHLCKE"
PATRONES = {
"email": r"[\w.+-]+@[\w-]+\.[\w.-]+",
"telefono": r"(?:\+34[\s.-]?)?\b[6789]\d{2}[\s.-]?\d{3}[\s.-]?\d{3}\b",
"iban": r"\bES\d{2}(?:\s?\d{4}){5}\b",
"dni": r"\b[XYZ]?\d{7,8}[\s-]?[A-Z]\b",
}
GENERICOS = ("info@", "contacto@", "soporte@", "hola@", "ventas@")
def dni_valido(valor: str) -> bool:
"""DNI o NIE: la letra final debe coincidir con el número (módulo 23)."""
v = valor.upper().replace("-", "").replace(" ", "")
if v[0] in "XYZ":
v = str("XYZ".index(v[0])) + v[1:]
return len(v) == 9 and v[:8].isdigit() and LETRAS[int(v[:8]) % 23] == v[8]
def enmascarar(texto: str) -> tuple[str, bool]:
"""Sustituye los datos personales por [<tipo> oculto]; devuelve el texto y True si se ha ocultado algo."""
encontrado = False
for tipo, patron in PATRONES.items():
def sustituir(m):
nonlocal encontrado
valor = m.group(0)
if tipo == "email" and valor.lower().startswith(GENERICOS):
return valor
if tipo == "dni" and not dni_valido(valor):
return valor
encontrado = True
return f"[{tipo} oculto]"
texto = re.sub(patron, sustituir, texto)
return texto, encontradoCoût estimé
Tokens d'entrée par appel : ≈ 916
| Modèle | Par appel | Pour 1 000 appels |
|---|---|---|
| Mistral Large · Mistral AI | 0,00040 € | 0,40 € |
| DeepSeek Flash · DeepSeek | 0,00024 € | 0,24 € |
| DeepSeek V4 Pro · DeepSeek | 0,00107 € | 1,06 € |
| GPT-6 Luna · OpenAI | 0,00008 € | 0,08 € |
| GPT-6 Sol · OpenAI | 0,00161 € | 1,61 € |
| Claude Sonnet 5.5 · Anthropic | 0,00161 € | 1,61 € |
| Claude Opus 5.5 · Anthropic | 0,00323 € | 3,23 € |
| 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--ocultar-datos-personales-rgpd
- Type
- Garde-fous
- Fichier
- ocultar-datos-personales-rgpd.guardrail.json
- Format
- JSON (application/json)
- Taille
- 1 990 octets (1,9 Ko)
- Encodage
- UTF-8
- Tokens estimés
- ≈ 546
- Langue
- Espagnol
- 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
ocultar-datos-personales-rgpd.guardrail.json
{
"format": "contextetech/guardrail/v1",
"id": "contextetech/ocultar-datos-personales-rgpd",
"description": "Salvaguarda RGPD que oculta DNI y NIE (letra comprobada), teléfonos +34, IBAN y correos personales en las respuestas de un asistente de IA. 12 pruebas.",
"tags": [
"rgpd",
"datos-personales",
"seguridad"
],
"license": "MIT",
"language": "es",
"type": "pii",
"action": "mask",
"rules": "Ocultar antes del envío cualquier dato que identifique o permita contactar con una persona:\n- correos electrónicos personales (ana.garcia@…);\n- teléfonos españoles, con o sin +34;\n- IBAN españoles;\n- DNI y NIE (se comprueba la letra de control).\nDejar pasar: direcciones genéricas (info@, soporte@…), nombres de empresas, importes, fechas, números de pedido.",
"cases": [
{
"input": "Puede escribir a ana.garcia@gmail.com para el seguimiento.",
"expected": "block"
},
{
"input": "Mi móvil es el 612 345 678.",
"expected": "block"
},
{
"input": "Llámeme al +34 912 345 678 mañana.",
"expected": "block"
},
{
"input": "Transferencia a ES91 2100 0418 4502 0005 1332.",
"expected": "block"
},
{
"input": "Mi DNI es 12345678Z.",
"expected": "block"
},
{
"input": "NIE del cliente: X1234567L.",
"expected": "block"
},
{
"input": "Escriba a soporte@contextetech.com para cualquier duda.",
"expected": "allow"
},
{
"input": "Su pedido n.º 458712 sale mañana.",
"expected": "allow"
},
{
"input": "El importe es de 1.250,00 € con IVA.",
"expected": "allow"
},
{
"input": "La cita es el 12/10/2026 a las 14:00.",
"expected": "allow"
},
{
"input": "La empresa Construcciones López S.L. aprobó el presupuesto.",
"expected": "allow"
},
{
"input": "Hola, ¿en qué puedo ayudarle?",
"expected": "allow"
}
]
}Vérifier un texte (Python)
import re
LETRAS = "TRWAGMYFPDXBNJZSQVHLCKE"
PATRONES = {
"email": r"[\w.+-]+@[\w-]+\.[\w.-]+",
"telefono": r"(?:\+34[\s.-]?)?\b[6789]\d{2}[\s.-]?\d{3}[\s.-]?\d{3}\b",
"iban": r"\bES\d{2}(?:\s?\d{4}){5}\b",
"dni": r"\b[XYZ]?\d{7,8}[\s-]?[A-Z]\b",
}
GENERICOS = ("info@", "contacto@", "soporte@", "hola@", "ventas@")
def dni_valido(valor: str) -> bool:
"""DNI o NIE: la letra final debe coincidir con el número (módulo 23)."""
v = valor.upper().replace("-", "").replace(" ", "")
if v[0] in "XYZ":
v = str("XYZ".index(v[0])) + v[1:]
return len(v) == 9 and v[:8].isdigit() and LETRAS[int(v[:8]) % 23] == v[8]
def enmascarar(texto: str) -> tuple[str, bool]:
"""Sustituye los datos personales por [<tipo> oculto]; devuelve el texto y True si se ha ocultado algo."""
encontrado = False
for tipo, patron in PATRONES.items():
def sustituir(m):
nonlocal encontrado
valor = m.group(0)
if tipo == "email" and valor.lower().startswith(GENERICOS):
return valor
if tipo == "dni" and not dni_valido(valor):
return valor
encontrado = True
return f"[{tipo} oculto]"
texto = re.sub(patron, sustituir, texto)
return texto, encontrado
Communauté
Pas encore de commentaire. Partagez votre retour, il aidera les suivants.