Typo-squatting detection is the automated identification of packages whose names are deliberately confusable with popular ones — requets for requests, lodahs for lodash, python-dateutil vs python3-dateutil — so that a single mistyped install command or a hallucinated dependency name delivers attacker-controlled code instead of the library you meant. The attack works because package installation is a name-based trust decision with no second factor: pip install gives whoever registered the string, and registries operate first-come-first-served on names.
It's a numbers game for attackers, and the numbers are good. requests sees hundreds of millions of downloads a month; if even 0.01% of manual installs fat-finger the name, a squatter collects thousands of executions of arbitrary code — because both npm and Python packaging support install-time hooks (preinstall scripts, setup.py) that run before you've imported anything.
What the attacks actually look like
A short history for calibration:
ctxandcolourama(PyPI, 2018) — the second one squattingcolorama, exfiltrating environment variables and AWS keys on install. The pattern hasn't changed since; only the payloads have.- The 2023–2024 npm crypto-stealer waves — hundreds of packages like
noblox.js-proxysquattingnoblox.js, shipping token grabbers aimed at game developers. go-module-mirrorBoltDB squat — a typosquatted Go module that carried a backdoor for roughly three years because the module proxy caches immutably; we did a full write-up in the boltdb typosquat post.- Slopsquatting (2024–2025) — the newest variant: LLM coding assistants hallucinate plausible-but-nonexistent package names at measurable rates (research across models put it near 20% of suggested packages in some ecosystems), and attackers register those names preemptively. Same mechanics, new discovery channel.
Related but distinct cousins: combosquatting (react-dom-router vs react-router-dom — real words, wrong order), namespace confusion (@types/lodahs), and dependency confusion, which exploits registry resolution order rather than human typing. Detection systems usually cover the whole family.
How detection engines decide a name is suspicious
No single signal is reliable; production detectors stack several:
| Signal | Technique | Example |
|---|---|---|
| Name distance | Damerau-Levenshtein distance of 1–2 against a top-N popularity list | lodahs → lodash (distance 1, transposition) |
| Keyboard adjacency | Weighted edits by physical key proximity | requeats — a sits next to s |
| Homoglyphs / confusables | Unicode skeleton comparison (UTS #39) | раramiko with a Cyrillic р |
| Popularity asymmetry | New, near-zero-download package near a giant | 40 downloads sitting one edit from 300M/month |
| Metadata mimicry | README/description copied from the target | Identical docs, different install hook |
| Behavioral | Install-script analysis: network calls, env reads, obfuscation | preinstall curl-piping to a paste site |
The name-distance layer alone drowns in false positives — legitimate forks, regional variants, and coincidences are everywhere (color and colors are both real). That's why the popularity asymmetry and behavioral layers matter: a name one edit from express is interesting; a name one edit from express, registered last Tuesday, with an obfuscated postinstall that reads ~/.npmrc and POSTs somewhere, is a verdict.
Registries run their own versions of this — npm has blocked new names that are too similar to existing popular packages since 2017 (the crossenv incident), and PyPI added Unicode normalization rules — but registry-side filtering is a floor, not a guarantee. The steady cadence of malicious-package reports from OSV and vendor research feeds demonstrates the leak rate.
Wiring detection into your pipeline
Detection you don't enforce is a threat-intel newsletter. Practical enforcement points, in order of value per effort:
- PR-time dependency review. Any diff to
package.json,requirements.txt,go.mod, or a lockfile triggers a check of new names against a squatting detector and malicious-package feeds (OSV.dev aggregates several). New-dependency review is rare enough that a human check on flagged names doesn't slow anyone down. - Registry proxy rules. If installs flow through an Artifactory/Nexus/Verdaccio proxy, block first-fetch of packages flagged by your detector, and quarantine anything younger than 72 hours. Most malicious packages are removed within two days of publication; a cooling-off window skips the exposure entirely.
- Lockfile scanning in CI. Scan the resolved tree, not just the manifest — squats arrive transitively too. This is where SCA tooling earns its place: Safeguard's malicious-package detection runs name-similarity and install-script behavioral checks against every new package entering the lockfile, which catches the class rather than the known list.
- Guardrails for AI-generated code. If your org uses coding assistants, add a CI rule: any dependency that didn't exist on the registry 30 days ago, or has under some threshold of weekly downloads, requires explicit human approval. This one rule neutralizes most slopsquatting risk.
One habit worth building at the individual level: copy-paste install commands from the project's official README instead of typing them. Boring advice; it's also how the majority of manual-install squats actually succeed.
What detection can't do
A well-run typosquatting program still doesn't cover: compromise of the legitimate package (account takeover — different attack, different controls), malicious code added in version 4.2.1 of a package you've depended on for years, or squats in ecosystems you don't scan (Docker Hub images and GitHub Actions get squatted too, and most tooling focuses on language registries). Treat name-based detection as one layer of package vetting, alongside pinning, provenance verification, and install-script sandboxing (npm install --ignore-scripts as a default is unreasonably effective).
Frequently asked questions
How common is typo-squatting really?
Continuously — researchers and registry teams remove thousands of malicious lookalike packages per year across npm and PyPI, and every large-scale study finds active campaigns. Most individual packages get few downloads, but the tail events (a squat trending or surviving months) are what the controls are priced against.
Does dependency pinning protect against typo-squatting?
Mostly no. Pinning guarantees you keep getting the package you resolved — including a squatted one if that's what entered the lockfile. Pinning prevents drift; typosquatting detection prevents the wrong name from entering in the first place. You need both.
Are private/internal package names at risk?
Yes, via the adjacent dependency-confusion attack: an attacker registers your internal package's name (or a near-variant) on the public registry. The defenses are registry scoping and resolution-order controls rather than name-distance detection, but mature tools check both patterns together.
Can I check a single package name quickly?
Reasonable quick checks: the package's download count and age on npmjs.com or PyPI, whether the repository link actually points to the project it claims, and OSV.dev for known-malicious flags. If a "popular" library has 60 weekly downloads and a repo link to an empty GitHub account, you have your answer.