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'{veredicto}', 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()