Typosquatting is one of the best-documented supply-chain attack techniques — publish a package one keystroke away from something popular, wait for developers to fat-finger npm install, and you have a foothold on their machine or CI pipeline with no CVE ever filed. It's been written about for years. What's less documented is a very simple question: right now, today, how many of those look-alike names are actually sitting on the registry?
So we checked. Not a sample, not an estimate — every single-character typo (omit a letter, double a letter, swap two adjacent letters — the three classic patterns) of the 30 most popular packages on npm and the 30 most popular on PyPI, queried live against each registry's real API, then cross-referenced every hit that's actually registered against OSV.dev's public malicious-package database.
The headline numbers
-
npm: 498 candidate typo names checked across 30 popular packages. 297 (59.6%) are currently registered, real packages. Of those 297, 97 (32.7%) are already documented as malicious in OSV's public database — not "suspicious," not "flagged by us," but carrying a real
MAL-orGHSA-advisory ID today. - PyPI: 582 candidate typo names checked across an equally popular 30-package set. 47 (8.1%) are registered. Zero are flagged malicious in OSV.
Same technique, same rigor, same day. One ecosystem where a third of the look-alikes you'd stumble into are already confirmed hostile; the other where the exposure — at least via this specific single-typo technique — is close to zero.
Methodology
This mirrors the exact detection method DepWarden uses in production: Damerau–Levenshtein edit-distance matching against a curated list of high-popularity package names. For this study we narrowed to the three typo patterns that account for the overwhelming majority of real-world typosquats (and match the classic examples — expresss, epxress — that show up in every writeup of this technique):
-
Omission — drop one character (
express→expres) -
Duplication — double one character (
express→expresss) -
Adjacent transposition — swap two neighboring characters (
express→epxress)
For each of the 30 most popular packages per ecosystem, every candidate generated by these three rules was checked against the real registry API (registry.npmjs.org for npm, pypi.org/pypi/.../json for PyPI) — a 200 response means it's a real, currently-published package. Every hit was then queried against api.osv.dev for any associated advisory. Nothing here is inferred or estimated; every count is a live HTTP response, checked on 25 August 2026.
What this deliberately doesn't claim: a registered look-alike name is not automatically malicious. Plenty of short candidate strings (vue → ve, next → net) collide with legitimate, completely unrelated packages that just happen to be short common words — npm alone has over 3 million packages, so some collision is inevitable. That's exactly why the OSV cross-reference matters: it's the difference between "this name exists" (weak signal) and "this specific package is a documented, named threat" (real finding). The 97/47 split above is the second kind.
Which popular packages attract the most confirmed-malicious squats
| Popular package | Confirmed-malicious typo variants found |
|---|---|
| request | 17 |
| lodash | 9 |
| express | 9 |
| chalk | 8 |
| commander | 8 |
| async | 8 |
| react | 7 |
| debug | 5 |
| bluebird | 5 |
| moment | 4 |
request — deprecated since 2020 but still one of the most-installed packages in npm history from years of accumulated dependents — tops the list by a wide margin. That's a pattern worth naming explicitly: an old, extremely popular, no-longer-actively-maintained package appears to be a more attractive squatting target than a young one, plausibly because the volume of developers who've typed its name from memory (and therefore mistyped it) over the package's lifetime is simply larger. We're not claiming causation from an n=30 sample, but the shape of the data points that way.
The full data, and how to re-run it yourself
The top-10 table above is the interesting slice, but a study you cannot check is just an assertion. So here is all of it — every candidate name, whether the registry says it is published, and every advisory id attached to it.
- Download the dataset (CSV) — one row per candidate, 1,098 rows across both ecosystems.
- Same data as JSON — includes the exact package lists used.
-
Re-run it yourself: the generator is in the DepWarden repo at
scripts/typosquat-study.mjs. The package lists are hard-coded in that file on purpose, so a re-run stays comparable rather than drifting with whatever is popular this week.
Re-running the whole study on 5 September 2026, eleven days after the original pass, reproduced it closely: npm 483 candidates, 284 registered (58.8%), 99 carrying an advisory (34.9% of registered); PyPI 615 candidates, 52 registered (8.5%), zero carrying an advisory. The per-package ranking came back identical — request still first by a wide margin at 17. Counts move by a few points between runs because packages get published and unpublished and OSV adds advisories; the finding does not.
The second data point: how healthy are the real packages?
Alongside the typosquat check, we pulled real OpenSSF Scorecard scores — an independent, automated 0–10 supply-chain-hygiene rating covering branch protection, CI presence, signed releases and 15 other checks — for the same 60 popular packages (59 resolved; one npm package's GitHub mapping didn't return a score).
- npm: average score 6.24/10. 20.7% score below 5. 6.9% score below 3.
- PyPI: average score 6.79/10. 10.0% score below 5. 0% score below 3.
Lowest-scoring packages checked: bluebird (2.0), debug (2.6), and request (3.4) on npm; psycopg2 (3.7), setuptools (4.4), and httpx (4.9) on PyPI. Worth noting: bluebird and debug — two of the lowest Scorecard scores in the whole sample — also both appear in the confirmed-malicious-squat table above. Again, not a causal claim from this sample size, but a genuinely interesting adjacency: the same handful of enormously popular, comparatively lower-maintenance-signal packages show up on both lists.
What this means if you ship software
The practical takeaway isn't "avoid npm" — it's that single-character typing mistakes in npm install carry real, non-hypothetical, currently-live risk today, at a rate (roughly 1 in 3 of the names you could plausibly typo into) that's higher than most developers would guess. PyPI's exposure, at least to this specific attack pattern, is measurably lower — worth knowing if you're weighing where a supply-chain review should focus first.
Three concrete things that actually help: pin exact versions in a lockfile so a one-off typo in a scratch command doesn't become a permanent dependency; use a scanner that checks for typosquat-shaped names as a distinct signal from CVE matching, since (by definition) none of these 97 malicious packages have a CVE; and treat an unfamiliar package with a name suspiciously close to something you know — one character off, one character doubled — as worth a second look before installing, especially if it has few downloads and a recent publish date.
DepWarden runs this same detection — Damerau-Levenshtein matching against a curated popular-package list per ecosystem — on every scan, free, no account required. Related reading: how npm typosquatting and dependency confusion attacks work, detecting typosquats in CI, what the OpenSSF Scorecard actually measures.
Top comments (0)