Skip to content
VishalPatil
Projects / Heron

Heron

Heron preview

A dual-tower phishing detector that reads the words and the logos at the same time, then fuses them into one verdict.

Stack used

  • Python
  • PyTorch
  • FastAPI
  • ML
  • Security

Why I built it

Phishing used to take skill. Now a generative model can spin up a convincing lure in seconds, clone a brand pixel for pixel, and ship fluent copy that sails past filters that only read text.

I wanted a detector that looks at an email the way a careful human would: read the language, glance at the logos, notice the weird sender domain, then decide. Heron is that system. Paste an email (or drop a .html / .eml file) and you get a phishing-or-legitimate call with a confidence score and the signals that drove it.

One click is all it takes. Heron catches it before you do.

The problem in one picture

Text-only filters fail when the scam looks clean on the page but wears a stolen brand mark. Image-only filters fail when there is no logo at all. Metadata heuristics alone are noisy. Real phishing mixes all three.

Architecture

So Heron runs three specialists on every email, then lets a fusion head learn which specialist to trust for that particular message.

How Heron reads an email

Input

Visible text is extracted, markup stripped, hyperlink patterns and HTML stats computed. Remote image URLs are never fetched.

Three specialists

Weighs language, urgency, and vocabulary from the words alone.

Catches visual brand impersonation in embedded logos. No logo means a zero-filled tensor.

Shortened URLs, caps ratio, suspicious domains, timing patterns.

Next step: concat 832-d

Decision

Learns to lean on text, logos, or metadata depending on the email. Softmax gives phishing vs legitimate with a confidence score.

Readable signals (urgency language, lookalike domains) ship with the score so the call is inspectable.

  • Input
  • Tower
  • Fusion

Hover a block for what it does. Three specialists look at the same email, then fuse into one verdict.

How a request travels

The app is a FastAPI service with the model in-process. No separate serving tier. One hop: parse, preprocess, classify, return.

Request path

From .html, .eml, or pasted text.

Only embedded images. Skipping remote URLs avoids SSRF on a public endpoint.

Text to 512 tokens, images to 224×224 ImageNet-normalized, 20 metadata features scaled.

Towers run, concatenate, classifier returns label and confidence.

One FastAPI process does parse, preprocess, and classify. No separate serving tier.

  1. Parse

    The email arrives as pasted text, .html, or .eml. Visible text comes out, markup goes away, and hyperlink patterns plus HTML statistics get computed.

  2. Extract logos

    Only embedded brand logos are decoded. Remote image URLs are never fetched. That keeps a public endpoint from becoming an SSRF toy. No logo means a zero-filled tensor for the image tower.

  3. Preprocess

    Text is tokenized against the training vocabulary and padded or truncated to 512 tokens. Images resize to 224×224 and get ImageNet normalization. Twenty metadata features are extracted and scaled.

  4. Fuse

    The three towers run. Their outputs concatenate into an 832-d vector. The fusion classifier produces two class logits.

  5. Verdict

    A softmax turns those into phishing vs legitimate with a 0 to 100% confidence, plus readable signals like shortened URLs, suspicious domains, and urgency language.

  • Python
  • PyTorch
  • FastAPI
  • ML
  • Security

What the numbers say

I trained and compared six models on a balanced set of 76,346 emails. The dual-tower fusion model lands at 99.45% accuracy and 0.999 AUC-ROC, past every single-modality baseline.

Model comparison on 76,346 emails

Baselines

Classic feature baseline.

Classic feature baseline.

Specialists

Phase 1 text specialist.

Phase 1 image specialist. Weak alone, useful in fusion.

Transfer-learning comparison baseline.

Final

23 false positives and 32 false negatives on ~10k validation emails.

Dual-tower fusion beats every single-modality baseline on the held-out split.

ModelAccuracyNotes
KNN (text features)81.71%Baseline
Logistic Regression80.00%Baseline
Custom Text CNN98.96%Phase 1 text specialist
Custom Image CNN76.30%Phase 1 image specialist
ResNet18 (transfer learning)97.43%Comparison baseline
Dual-tower fusion99.45%Final model

On the phishing class (the expensive one to miss):

MetricValue
Precision99.5%
Recall99.4%
F199.4%
False positives / negatives23 / 32 on ~10k validation emails

Why the image tower still earns its seat

76.30% alone looks weak next to the text CNN. In fusion it still helps, because brand impersonation shows up in pixels when the wording is carefully bland.

The data underneath

The email corpus mixes four public sources (CEAS_08, Enron, Nazario, Nigerian Fraud) into 76,346 deduplicated, labelled emails, roughly 48% phishing and 52% legitimate. The logo corpus is OpenLogo: 72,652 images across 352 brand classes.

Real phishing HTML with embedded logos is scarce, so the test set was also augmented with generative-AI phishing emails that keep authentic structure and logos. That is the world the model has to survive in.

Weights ship from a public Hugging Face repo at startup. The service is containerized and runs on Cloud Run.

Design choices I would defend

Learned fusion, not a fixed rule

The fusion layer weights each modality per email. It leans on text when the language screams urgency, on logos when the brand mark and the sender domain disagree, and on metadata for clean-looking edge cases.

Graded confidence

A binary flag hides uncertainty. Every verdict carries a percentage and the signals that mattered, so a reviewer can see why.

No remote image fetch

Skipping remote logos costs some recall on image-only cues. It also keeps a public scan endpoint from fetching arbitrary URLs for an attacker.

In-process model

One FastAPI process owns parse through classify. Fewer moving parts, one log stream, one deploy.

Design rationale

Why dual-tower instead of one big encoder? Phishing cues are heterogeneous. A single encoder tends to under-represent one of the modalities. Separate towers let each pathway specialize, and fusion combines the strengths.

How do you validate beyond a single accuracy number? Accuracy, AUC, precision, recall, and F1 on the phishing class, plus a raw false-positive / false-negative count on the held-out split. Thresholds get chosen for the cost of a miss, not for a pretty aggregate.

What about the precision / recall tradeoff? Missing a phishing email is worse than bothering someone with a false alarm, but flooding people with false alarms kills trust. The operating point sits where recall stays high without making the product unusable.

What I would tell you next

If you want the full math and ablations, the research paper and architecture report are linked as chips at the top. The live scan surface is at heron.v-ai.org.

Sole builder

I designed, trained, evaluated, documented, and shipped every part of this. Spec-driven work against my own written specs, with an append-only session log and a teaching journal in the repo.