A Chrome and Firefox extension that warns you before a scam website gets your passwords, card details or money.
A fake PayPal login on a look-alike domain. The banner explains why, in plain words, and offers a one-click way out.
It plays above without sound. Click it for the full video with music. A fake "your account has been limited" text, the warning that appears when the link is opened, a marketplace chat scam, and the honest part: it will miss some scams, so stay alert. Made for people who are not technical, so share it with parents and grandparents.
π΅πΉ Em portuguΓͺs: vΓdeo com a burla da encomenda dos CTT. Share text and how to re-render: docs/video/.
Every page you open is scored from 0 to 100 for the traces scams leave behind: a domain registered days ago, a brand name hidden in the address, a login form that posts somewhere else, a logo hot-linked from the real company, the wording of a "your account has been suspended" page, a courier that will "collect the item and pay you by link". When the score crosses a threshold a banner appears at the top of the page:
| Score | Banner | Meaning |
|---|---|---|
| 60 β 100 | π΄ This website is likely a scam | Several strong, independent indicators. Do not enter anything. |
| 35 β 59 | π This website looks suspicious | Some indicators. Look closer before trusting it. |
| 0 β 34 | none | The popup shows the score and lists any minor observations. |
The toolbar shield itself changes colour, with no number on it: π’ green with β when nothing was found, π‘ yellow when there are minor signals, π΄ red when a warning banner is showing, grey with β on a site you trust, and blue-grey on pages that were not checked.
Do not trust it blindly. Scam Detector recognises the traces of known scams. It will miss some, and it will most likely miss new schemes nobody has identified yet. No warning does not mean safe. A welcome page says this on first install.
Everything runs inside the browser. The only network traffic is a domain-registration lookup (domain name only), a periodic download of PhishTank's public reports feed, and, once you enter your own API key, a Google Safe Browsing check (on by default, inactive without a key). See Privacy.
The interface follows the browser language. Every banner, button, popup line, settings text, signal explanation and chat-scam advice ships in English, Portuguese, Spanish and French (_locales/), and the same four languages are used for detecting scam wording on pages and in chats.
![]() PortuguΓͺs. The CTT parcel-fee scam with the banner and explanations in Portuguese. |
![]() FranΓ§ais. The fake PayPal login seen from a French browser. |
![]() Popup, Portuguese. |
![]() Popup, Spanish. |
![]() Popup, French. |
flowchart LR
P[Page loads] --> C[Content script<br/>collects page signals]
C --> B[Service worker]
B --> R1[RDAP<br/>domain age]
B --> R2[PhishTank feed<br/>local hash lookup]
B --> R3[Safe Browsing<br/>needs own key]
B --> L[Look-alike & brand checks]
R1 & R2 & R3 & L --> S[Scoring<br/>0β100]
S -->|β₯ 35| W[Banner + badge]
S -->|< 35| Q[Badge only]
C -. on marketplaces .-> M[Chat monitor] --> W
Each observation becomes a weighted signal; weights add up, strong independent signals reinforce each other, and the total is clamped to 100. Pages on the real domains of known brands, on local networks and on domains you trust are never flagged, with one exception: a verified phishing report for the exact page overrides the brand shortcut, so phishing hosted on Google Docs or Dropbox still warns.
Domain-level signals
| Signal | Description | Weight |
|---|---|---|
| Domain age | Registration date via RDAP. < 7 days: 35, < 30 days: 30, < 90: 22, < 180: 14, < 1 year: 8 | up to 35 |
| Brand impersonation | The hostname contains, typosquats or homoglyph-mimics one of ~180 commonly impersonated brands (banks, payments, crypto, couriers, tax authorities, telecoms, marketplaces, big tech) and is not one of that brand's real domains. Covers paypal-secure-login.com, paypa1.com, secure.paypal.com.evil.net, gooogle.com, xn--pypal-4ve.com (Cyrillic Π°), micros0ft-support.live, amazon-de.top |
28β35 |
| IDN / punycode | Non-Latin hostname without a brand match | 10 |
| Raw IP host | Site addressed by IP instead of a name | 25 |
| Risky TLD | TLDs with a very high abuse share (.zip, .top, .xyz, .tk, .icu, .sbs, β¦) |
4β14 |
| Phishing words in hostname | login, verify, secure, account, redelivery, airdrop, β¦ |
6 each, max 18 |
| Structure | Many hyphens, deep subdomains, very long host, mixed digits, random-looking names | 4β8 each |
| No HTTPS | Unencrypted page; much worse if it asks for credentials | 10 / 25 |
Reputation sources
| Signal | Description | Weight |
|---|---|---|
| Verified phishing report | The page (or a path prefix of it) is in PhishTank's verified-online feed. A local copy (~50k addresses, refreshed every 6 hours, stored as truncated SHA-256 hashes) is matched in the browser, so visited URLs never leave the device. On shared platforms (Google Docs/Sites, link shorteners, form builders, free hosting) only exact-page matches count | 75 |
| Phishing reports on this host | The host has verified reports and is not a shared platform | 45 |
| Google Safe Browsing (on by default, needs a key) | With your own API key, the Lookup API is asked about each page. Warnings carry the "Advisory provided by Google" attribution and the links Google's terms require; answers are cached at most 30 minutes | 75 |
Page-level signals
| Signal | Description | Weight |
|---|---|---|
| Credential / card form in a bad context | Password or card field on a look-alike domain, a days-old domain, a free hosting sandbox, or an IP host | 12β22 |
| Seed phrase request | Input asking for a wallet recovery phrase / private key | 35 |
| Cross-domain submission | Credentials posted to a different registrable domain, or over plain HTTP | 20 |
| Brand mismatch | Page title / heading / copyright claims to be a brand the domain does not belong to (ambiguous names like Apple, Chase, Visa need an account/login context) | 10, 22 with a form |
| Hot-linked brand assets | Favicon or logos loaded from the real brand's servers | 15β20 |
| Social-engineering language | Urgency, prizes, tech-support alerts, parcel-fee lures, fake "100% safe" claims | 5β15 per category |
| Money claims | Templates of well-known money frauds: unrealistic investment returns, advance fees to "release" funds, no-credit-check loans with upfront fees, job offers that charge a registration fee, overpayment refunds, lottery / inheritance / unclaimed funds, fake escrow, crypto doubling, gift-card-only payment, tiny "verification fees" | 8β14 per category |
| Money claims + a way to take your money | Card or account form next to money claims, several fraud templates on one page, money claims on a days-old or free-hosted site | up to 32 |
| Fake navigation / deceptive links | Most links go nowhere; link text shows one domain, href another |
6β12 |
| Misc | Right-click disabled on a login page, decorative "secure" badges, auto-redirect to another domain, many third-party frames | 5β8 |
All language signals together are capped at 40 points. Patterns exist in English, Portuguese, Spanish and French, matched accent-insensitively.
Marketplace chat schemes (on by default)
| Scheme | Typical message |
|---|---|
| Courier pick-up | "I'm abroad, I'll send a courier to collect it, the transport company pays you" |
| Payment link / card details to receive money | "Click the link to receive the payment and enter your card details" |
| Look-alike link | A link in the chat whose domain imitates the marketplace or a payment brand |
| Fee to release the money | "The payment is held by the platform until you pay the transport insurance" |
| Forward the SMS code | "Send me the verification code you just received" |
| Overpayment | "I sent too much by mistake, refund the difference" |
| Rental deposit before viewing | "Pay the deposit first, the keys will be sent by courier, Airbnb will handle it" |
| Fake payment proof / upgrade | "I've paid, see the screenshot. It's pending until you upgrade to a business account" |
| Pay upfront via untraceable channel | "Bank transfer / MB WAY / Bizum / PayPal friends & family / gift cards first, then I ship" |
| Off-platform + abroad + pressure | Weak alone, an alert together |
The chat text never leaves the browser. On sites without a recognisable chat area, only the strong schemes are applied to the page text.
Install Scam Detector from the Chrome Web Store, the easiest way, with automatic updates.
To install a specific build by hand instead, use the Releases page:
Every release has two packages built from the same code: scam-detector-<version>-chrome.zip and scam-detector-<version>-firefox.zip (check them against SHA256SUMS.txt if you like).
Chrome, Edge, Brave and other Chromium browsers
- Download the Chrome zip and unzip it.
- Open
chrome://extensions, enable Developer mode (top right). - Click Load unpacked and select the unzipped folder (the one containing
manifest.json).
Developers can load the repository folder directly the same way.
Firefox 140+ (desktop) and Firefox for Android 142+
Release builds of Firefox only install add-ons signed by Mozilla, so the Firefox zip is meant for the Firefox Add-ons submission (see store-listing/firefox.md) and for testing: open about:debugging#/runtime/this-firefox, click Load Temporary Add-on⦠and pick the zip. It stays loaded until Firefox restarts. If Firefox has not given the extension access to websites, the popup says so and offers an Allow access to websites button.
The shield icon shows the risk score of the current tab (red, amber, or a green tick for known and trusted sites). Click it for the full list of findings, to re-scan, or to trust a site.
Open the popup β Settings:
- Non-tech terms (on by default): every warning and setting in simple words; off shows the technical wording.
- Danger / warning thresholds, and whether the amber banner is shown.
- RDAP domain-age lookup on/off and cache clearing.
- Phishing-reports feed on/off, optional PhishTank app key, manual update.
- Google Safe Browsing on/off (on by default; it only runs once you enter your own API key β restrict the key to the extension's origin in Google Cloud).
- Marketplace chat monitor on/off (on by default) and extra sites to monitor.
- Trusted domains.
- Extra brands to protect, one per line:
Name | keyword1, keyword2 | realdomain.com. - About: version, author (pedrovicente.pt), feedback link, and donations via Revolut or Buy me a beer.
| Data | Where it goes | Default |
|---|---|---|
| Registrable domain name (never the URL or page content) | rdap.org, which redirects to the domain's registry, to learn the registration date. Cached 7 days |
on, can be disabled |
| Nothing about you | data.phishtank.com: the extension downloads the public reports feed a few times a day. Matching is local |
on, can be disabled |
| Full page URL | safebrowsing.googleapis.com, only if you enable it and provide your own key |
off |
| Page content, chat text, settings | Processed in memory; settings sync via the browser's sync (Chrome sync or Firefox Sync); per-tab results live in session storage | never sent |
No analytics, no accounts, no remote code. The full policy is in store-listing/privacy-policy.md.
npm test # unit tests + store-listing consistency check + catalogue report
npm run e2e # loads the extension into Chromium with Playwright and checks every scenario
npm run icons # regenerate icons/*.png
npm run package # dist/scam-detector-<version>-chrome.zip and -firefox.zip, runtime files only
node tools/firefox-manifest.js # print the generated Firefox manifest
npm run store-assets # regenerate store-listing/assets/ (Chrome) and assets-firefox/ (Firefox) PNGs
node tools/docs-screenshots.js # regenerate the README screenshots in docs/
node tools/catalogue-report.js [--feed online-valid.csv] # catalogue stats, stale-review warning, brands missing vs. PhishTank targets
No build step and no runtime dependencies. The library files use a tiny UMD wrapper so the same code runs in the Chrome service worker (importScripts), the Firefox background script (listed in the generated manifest), in content scripts and under Node for tests. The Firefox package differs from the Chrome one only in manifest.json, which tools/firefox-manifest.js derives from the Chrome manifest at package time: background scripts instead of a service worker, the Gecko add-on ID, minimum Firefox versions and the data-collection declaration.
manifest.json
src/background.js service worker: RDAP lookup, PhishTank feed, Safe Browsing, scoring, badge, messaging
src/content.js collects page observations, chat monitor, renders the banners (closed shadow DOM)
src/lib/domain.js hostname parsing (compact public-suffix subset), Levenshtein, entropy
src/lib/punycode.js IDN decoding
src/lib/brands.js impersonated-brand catalogue
src/lib/lookalike.js typosquat / homoglyph / embedded-brand detection
src/lib/tlds.js TLD risk weights
src/lib/phrases.js scam language + money-claim patterns (en/pt/es/fr, accent-insensitive)
src/lib/chatSchemes.js marketplace chat scam scripts (en/pt/es/fr) and marketplace list
src/lib/urlmatch.js phishing-feed index (hashed) and local URL/host matching
src/lib/i18n.js chrome.i18n wrapper; signals and chat schemes carry message keys
_locales/ UI strings in en, pt, es, fr
src/lib/pageSignals.js DOM observation collector
src/lib/scoring.js signals β score β verdict
popup/, options/ UI
tools/ icon generator, packager, screenshot generators, catalogue report, e2e script
test/ unit tests
catalogue/ review log for the scam catalogue
.github/workflows/ CI and release pipelines
store-listing/ Chrome Web Store submission kit (+ firefox.md for Firefox Add-ons)
.claude/skills/ update-scam-catalogue skill
Detection is only as good as the lists it ships. The update-scam-catalogue skill (.claude/skills/update-scam-catalogue/SKILL.md) walks an agent through reviewing current reports from Banco de Portugal, PJ, CNCS, DECO, INCIBE, cybermalveillance.gouv.fr, Action Fraud, the FTC, APWG, Spamhaus and PhishTank's own target statistics, adding what is missing with tests in all four languages, and logging the review in catalogue/last-review.json. AGENTS.md requires it before finishing any change, and npm test warns when the last review is older than 30 days.
Two GitHub Actions workflows in .github/workflows/:
- CI (
ci.yml) runs on every push and pull request: unit tests, the store-listing consistency check, the catalogue report, the Chrome and Firefox package builds attached as an artifact,web-ext linton the Firefox package, and the Playwright end-to-end scenarios in Chromium with their screenshots. - Release (
release.yml) runs on every push tomain(or on manual dispatch). If no tag exists for the manifest version it runs the tests, buildsscam-detector-<version>-chrome.zipandscam-detector-<version>-firefox.zip, lints the Firefox package withweb-ext lint, writesSHA256SUMS.txt, takes the release notes from the What's new section ofstore-listing/listing.md, tagsv<version>and publishes a GitHub Release with both zips and install instructions for each browser. Bumping the version is therefore the whole release procedure; pushes that do not bump it are a no-op.
The extension is live at https://chromewebstore.google.com/detail/scam-detector/lgppgajjfgffahljkkgcnjjelnbgjaod. Everything for the submission is in store-listing/: listing text, permission justifications, privacy policy (Markdown and a hostable HTML copy), data-usage answers, distribution notes and generated screenshots and promo tiles. npm run package builds the upload zips (Chrome and Firefox); the Firefox Add-ons submission notes are in store-listing/firefox.md; npm run check-listing (part of npm test) fails when the kit disagrees with the code, and AGENTS.md tells coding agents to update the kit in the same commit as any change that affects it.
Only sources whose published terms allow this use are called. PhishTank data is free to use, including commercially, per its published terms; Google Safe Browsing is free for non-commercial use with attribution. ScamAdviser (paid B2B API only), ScamWise and ScamChecker.app (terms prohibit automated access), OpenPhish (non-commercial, no redistribution) and the VirusTotal public API (no products) are deliberately not used.
- Heuristics, not a blocklist: a well-made scam on an old domain with careful wording can score low, and an aggressive marketing page on a cheap TLD can score amber. Treat the banner as a prompt to look closer, not a verdict.
- Some registries (notably
.deand many ccTLDs) do not publish registration dates over RDAP, so domain age is unknown there. - The public-suffix list is a curated subset; unusual multi-label suffixes may be split incorrectly.
- Chat-area detection relies on generic markup; on unusual sites only the strong schemes apply to the whole page.
- Known catalogue gaps: Imovirtual, Standvirtual, Continente, Pingo Doce, Worten, Via Verde and the MB WAY request-versus-send chat pattern.
MIT















