Spaces:
Sleeping
Sleeping
File size: 4,821 Bytes
d889b51 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | import re
import gradio as gr
# ---------------------------
# 1. REGEX PATTERNS
# ---------------------------
EMAIL_RE = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b")
URL_RE = re.compile(r"\bhttps?://\S+|\bwww\.\S+\b", re.IGNORECASE)
IP_RE = re.compile(r"\b\d{1,3}(?:\.\d{1,3}){3}\b")
DATE_RE = re.compile(r"\b(?:\d{1,2}[\/\-.]\d{1,2}[\/\-.]\d{2,4}|\d{4}-\d{2}-\d{2})\b")
# téléphones un peu larges, on filtrera les trop courts
PHONE_RE = re.compile(r"(?:\+?\d[\d\s().-]{5,}\d)")
# cartes bancaires : on détecte 13 à 19 chiffres avec séparateurs
CARD_RE = re.compile(r"\b(?:\d[ -]*?){13,19}\b")
# SSN US basique
SSN_RE = re.compile(r"\b\d{3}-\d{2}-\d{4}\b")
# ---------------------------
# 2. UTILITAIRES
# ---------------------------
def luhn_valid(number: str) -> bool:
"""Vérifie un numéro de carte avec Luhn (simple)."""
digits = [int(d) for d in number if d.isdigit()]
if len(digits) < 13 or len(digits) > 19:
return False
checksum = 0
parity = len(digits) % 2
for i, d in enumerate(digits):
if i % 2 == parity:
d = d * 2
if d > 9:
d -= 9
checksum += d
return checksum % 10 == 0
def mask_value(value: str, style: str = "tag") -> str:
"""
style:
- tag -> [PII]
- stars -> ********
- keep_len -> même longueur mais *
"""
if style == "tag":
return "[PII]"
elif style == "stars":
return "*" * len(value)
elif style == "keep_len":
return "".join("*" if not c.isspace() else c for c in value)
return "[PII]"
# ---------------------------
# 3. ANONYMISATION
# ---------------------------
def anonymize(text: str, style: str):
if not text.strip():
return "Paste or type a text to anonymize.", "{}"
counts = {
"email": 0,
"url": 0,
"ip": 0,
"date": 0,
"phone": 0,
"card": 0,
"ssn": 0,
}
# 1) Emails
def repl_email(m):
counts["email"] += 1
return mask_value(m.group(), style)
text = EMAIL_RE.sub(repl_email, text)
# 2) URLs
def repl_url(m):
counts["url"] += 1
return mask_value(m.group(), style)
text = URL_RE.sub(repl_url, text)
# 3) IPs
def repl_ip(m):
counts["ip"] += 1
return mask_value(m.group(), style)
text = IP_RE.sub(repl_ip, text)
# 4) Dates
def repl_date(m):
counts["date"] += 1
return mask_value(m.group(), style)
text = DATE_RE.sub(repl_date, text)
# 5) SSN
def repl_ssn(m):
counts["ssn"] += 1
return mask_value(m.group(), style)
text = SSN_RE.sub(repl_ssn, text)
# 6) Cartes bancaires (avec Luhn)
def repl_card(m):
raw = m.group()
digits = "".join(ch for ch in raw if ch.isdigit())
if luhn_valid(digits):
counts["card"] += 1
return mask_value(raw, style)
return raw # pas une vraie carte
text = CARD_RE.sub(repl_card, text)
# 7) Téléphones
def repl_phone(m):
raw = m.group()
# on nettoie
digits = "".join(ch for ch in raw if ch.isdigit())
if len(digits) < 6: # trop court pour être un vrai numéro
return raw
counts["phone"] += 1
return mask_value(raw, style)
text = PHONE_RE.sub(repl_phone, text)
# petit résumé JSON
import json
stats = json.dumps({k: v for k, v in counts.items() if v > 0}, indent=2, ensure_ascii=False)
return text, stats or "{}"
# ---------------------------
# 4. GRADIO UI
# ---------------------------
with gr.Blocks(title="PII-Shield — Anonymizer") as demo:
gr.Markdown("# 🛡️ PII-Shield — Text Anonymizer")
gr.Markdown("Collez un SMS, un e-mail ou un texte administratif. L’outil masque automatiquement emails, téléphones, URLs, IPs, dates, SSN et cartes bancaires (Luhn).")
with gr.Row():
inp = gr.Textbox(lines=10, label="Texte à anonymiser")
style = gr.Radio(
["tag", "stars", "keep_len"],
value="tag",
label="Style de masquage",
info="tag = [PII], stars = ********, keep_len = même longueur"
)
btn = gr.Button("Analyser / Masquer")
out = gr.Textbox(lines=10, label="Texte anonymisé")
stats = gr.JSON(label="Éléments détectés")
examples = [
"Salut Marie, écris-moi à marie.dupont@example.com ou appelle au +33 6 12 34 56 78. RDV le 12/05/2024.",
"Payment card: 4111 1111 1111 1111, IP: 192.168.0.1, site: https://example.org",
"US client SSN: 123-45-6789"
]
gr.Examples(examples=examples, inputs=inp, outputs=[out, stats])
btn.click(anonymize, inputs=[inp, style], outputs=[out, stats])
if __name__ == "__main__":
demo.launch() |