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.
Regeln
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.
Testfälle
| Eingabe | Erwartet |
|---|---|
| Puede escribir a ana.garcia@gmail.com para el seguimiento. | Blockieren |
| Mi móvil es el 612 345 678. | Blockieren |
| Llámeme al +34 912 345 678 mañana. | Blockieren |
| Transferencia a ES91 2100 0418 4502 0005 1332. | Blockieren |
| Mi DNI es 12345678Z. | Blockieren |
| NIE del cliente: X1234567L. | Blockieren |
| Escriba a soporte@contextetech.com para cualquier duda. | Zulassen |
| Su pedido n.º 458712 sale mañana. | Zulassen |
| El importe es de 1.250,00 € con IVA. | Zulassen |
| La cita es el 12/10/2026 a las 14:00. | Zulassen |
| La empresa Construcciones López S.L. aprobó el presupuesto. | Zulassen |
| Hola, ¿en qué puedo ayudarle? | Zulassen |
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, encontradoGeschätzte Kosten
Eingabe-Tokens pro Aufruf : ≈ 916
| Modell | Pro Aufruf | Pro 1.000 Aufrufe |
|---|---|---|
| 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 € |
| Selbst gehostetes Open-Source-Modell (Ollama, vLLM) | 0 € API-Kosten (nur Serverkosten) | |
Nur Eingabekosten, ohne die Antwort des Modells. Öffentliche Standardpreise der Anbieter (Mistral AI, DeepSeek, OpenAI, Anthropic) von USD in Euro umgerechnet zum EZB-Kurs vom 29. September 2026. Schätzung: 1 Token ≈ 3,6 Zeichen.
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 →
Statistiken
Gefällt mir : 0 · Kommentare : 0
Klicken Sie auf einen Zähler, um ihn ein- oder auszublenden; fahren Sie über das Diagramm für Details. Ein Aufruf pro Besucher und Tag, ohne Bots; Zählung seit dem 30. September 2026.
- Kennung
- contextetech--ocultar-datos-personales-rgpd
- Typ
- Schutzregeln
- Datei
- ocultar-datos-personales-rgpd.guardrail.json
- Format
- JSON (application/json)
- Größe
- 1.990 Bytes (1,9 KB)
- Kodierung
- UTF-8
- Geschätzte Tokens
- ≈ 546
- Sprache
- Spanisch
- Lizenz
- MIT
- Kommerzielle Nutzung
- Erlaubt
- Personenbezogene Daten
- Fiktiv (erfunden)
- Version
- 1.0
- Veröffentlicht am
- 3. Oktober 2026 um 05:20
- Aktualisiert am
- 3. Oktober 2026 um 05:20
- Speicherung
- in der Datenbank
- Nutzungen
- 0
- Gefällt mir
- 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"
}
]
}Text prüfen (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
Community
Noch keine Kommentare. Teile deine Erfahrung, sie hilft den Nächsten.