disarm

Mixed script & whole-script confusables

Check a lookalike domain

Enter a hostname to see it label by label, with the script of each and whether it folds onto a Latin name. One flag cannot answer this: the same suspicious fires for a Cyrillic imitation of a brand and for an ordinary Russian domain.

The tool

The example spells a brand entirely in Cyrillic, so every character of the label agrees with every other and a mixed-script check reports nothing — yet it folds exactly onto the Latin name. Compare it with the others below.

Enter a hostname to check it.

Nothing is uploaded. The engine runs inside this page.

Label by label

Each label with the scripts it uses. The top-level domain is shown too, because its script is half the question.

Folds to

The canonical form. When this is a familiar name, that is the point.


      

Loading the engine…

Why one flag is not enough

Measured with disarm. Note the suspicious column: it cannot tell the second row from the fourth.

Hostnamesuspicious mixed_scriptwhole_script_confusable folds to
paypal.comfalsefalsefalsepaypal.com
раураӏ.comtruefalsetruepaypal.com
раураӏ.рфtruefalsetruepaypal.pф
яндекс.рфtruefalsefalseяндekc.pф
рaypal.comtruetruefalsepaypal.com

Rows two and four both report suspicious. One is a Cyrillic imitation of a payment provider; the other is Russia's largest search engine. Blocking on that flag alone rejects the second, and ignoring it lets the first through.

whole_script_confusable separates them, because the imitation folds onto a Latin name and the real domain does not. The third row shows why the top-level domain matters as well: the same Cyrillic label under .рф is what a site written in Russian looks like, so the usable policy is a label that folds onto Latin, under a Latin top-level domain — which is what per-label results let you express.

Row five is the easier case and the one browsers already handle: two scripts inside one label, which no ordinary name does.

The same thing in your own code

Each block is a file CI compiles and runs, so none can quietly stop working, and all seven print the same line. disarm on GitHub →

# Tell a spoofed domain from a real one written in another script.
#   pip install disarm
from disarm import is_suspicious_hostname

# paypal spelled entirely in Cyrillic: er, a, u, er, a, palochka. Every character
# agrees with every other, so nothing is mixed-script.
SPOOF = "раураӏ.com"
LEGIT = "яндекс.рф"          # Russia's largest search engine

spoof_flag, spoof = is_suspicious_hostname(SPOOF)
legit_flag, legit = is_suspicious_hostname(LEGIT)

# The blunt signal cannot tell them apart — it fires for both.
assert spoof_flag and legit_flag, "suspicious flags both"
assert not spoof.mixed_script, "the spoof is not mixed-script; every letter is Cyrillic"

# Whole-script confusability is what separates them.
assert spoof.whole_script_confusable, "the spoof folds onto a Latin name"
assert not legit.whole_script_confusable, "the real domain does not"
assert spoof.canonical == "paypal.com", spoof.canonical

print("ok: both flagged suspicious; whole-script confusability separates them")