Spaces:
Sleeping
Sleeping
| import os | |
| import re | |
| import joblib | |
| import gradio as gr | |
| import nltk | |
| nltk.download("stopwords", quiet=True) | |
| nltk.download("punkt_tab", quiet=True) | |
| MODEL_PATH = os.path.join(os.path.dirname(__file__), "models", "best_model.joblib") | |
| THRESHOLD = 0.55 | |
| URL_TOKEN = "url_token" | |
| EMAIL_TOKEN = "email_token" | |
| NUM_TOKEN = "num_token" | |
| _RE_URL = re.compile(r"https?://\S+|www\.\S+", re.IGNORECASE) | |
| _RE_EMAIL = re.compile(r"[\w.+-]+@[\w-]+\.[a-z]{2,}", re.IGNORECASE) | |
| _RE_NUM = re.compile(r"\b\d[\d\s.,/-]*\d\b|\b\d\b") | |
| _RE_PUNCT = re.compile(r"[^\w\s]") | |
| _RE_WS = re.compile(r"\s+") | |
| try: | |
| from nltk.corpus import stopwords | |
| _STOPWORDS = set(stopwords.words("english")) | |
| except Exception: | |
| _STOPWORDS = set() | |
| def clean_text(text): | |
| if not text: | |
| return "" | |
| text = str(text).lower() | |
| text = _RE_URL.sub(f" {URL_TOKEN} ", text) | |
| text = _RE_EMAIL.sub(f" {EMAIL_TOKEN} ", text) | |
| text = _RE_NUM.sub(f" {NUM_TOKEN} ", text) | |
| text = _RE_PUNCT.sub(" ", text) | |
| tokens = [t for t in _RE_WS.sub(" ", text).strip().split() if t not in _STOPWORDS] | |
| return " ".join(tokens) | |
| _model = joblib.load(MODEL_PATH) | |
| def classify(text, threshold): | |
| if not text or not text.strip(): | |
| return "No input", "—", "—" | |
| cleaned = clean_text(text) | |
| proba = float(_model.predict_proba([cleaned])[:, 1][0]) | |
| label = int(proba >= threshold) | |
| if label == 1: | |
| veredicto = "PHISHING" | |
| color = "color: #c0392b; font-weight: bold; font-size: 1.4em;" | |
| else: | |
| veredicto = "LEGITIMATE" | |
| color = "color: #27ae60; font-weight: bold; font-size: 1.4em;" | |
| return ( | |
| f'<span style="{color}">{veredicto}</span>', | |
| f"{proba:.1%}", | |
| f"{1 - proba:.1%}", | |
| ) | |
| EXAMPLES = [ | |
| ["Congratulations! You've been selected for a $1,000,000 prize. Click here to claim now: http://win-prize.xyz. Act fast, expires today!", 0.55], | |
| ["Hi team, please find attached the Q3 financial report for your review. Let me know if you have any questions. Best regards, Sarah.", 0.55], | |
| ["URGENT: Your account has been suspended. Verify your identity immediately at http://secure-bank-login.net or lose access permanently.", 0.55], | |
| ["Hey, are we still on for lunch tomorrow at 12:30? Let me know if the time works for you.", 0.55], | |
| ] | |
| with gr.Blocks(theme=gr.themes.Soft(), title="Phishing Detector") as demo: | |
| gr.Markdown( | |
| """ | |
| ## Phishing Email Detector | |
| Paste any email (subject + body) and the model will classify it as phishing or legitimate. | |
| SVM classifier trained on ~47k emails (Enron, SpamAssassin, Nazario). F1 = 0.987 on held-out test set. | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| txt = gr.Textbox( | |
| lines=10, | |
| label="Email text", | |
| placeholder="Paste email content here...", | |
| ) | |
| threshold = gr.Slider( | |
| 0.1, 0.9, value=0.55, step=0.05, | |
| label="Decision threshold (lower = more sensitive)", | |
| ) | |
| btn = gr.Button("Classify", variant="primary") | |
| with gr.Column(scale=1): | |
| veredicto = gr.HTML(label="Result") | |
| prob_phishing = gr.Textbox(label="Phishing probability") | |
| prob_legit = gr.Textbox(label="Legitimate probability") | |
| btn.click(classify, inputs=[txt, threshold], outputs=[veredicto, prob_phishing, prob_legit]) | |
| txt.submit(classify, inputs=[txt, threshold], outputs=[veredicto, prob_phishing, prob_legit]) | |
| gr.Examples(examples=EXAMPLES, inputs=[txt, threshold], label="Examples") | |
| demo.launch() | |