There is no exploit here and no vulnerability to patch. Someone registers a package name one keystroke away from a library you use, and waits for a developer to type it. The defence is detection, because there is nothing to fix.

It is one of five routes into a software supply chain, and the cheapest. Typosquatting is old on the web — domains one letter from a bank's — and it transferred to package registries almost unchanged, with one important difference. Visiting a lookalike domain shows you a page you might notice is wrong. Installing a lookalike package runs code on your machine before you look at anything.

The four shapes it takes

Attackers do not pick names randomly. The variants cluster into a handful of patterns, all of them chosen because they survive a glance:

  • Transposition — two adjacent letters swapped, as in reqeusts for requests. Almost invisible when reading quickly, because we read word shapes rather than letters.
  • Omission or doubling — a dropped or repeated character, such as loadash or expresss.
  • Separator confusion — hyphen versus underscore versus nothing. Ecosystems differ in convention, so a developer moving between them guesses wrong regularly.
  • Visual confusables — ASCII characters that read as one another at a glance: rn for m, 1 for l, 0 for O. True Unicode homoglyphs, such as a Cyrillic а, are the domain-name version of this attack and are blocked by both npm and PyPI, which restrict package names to ASCII.

Why the payload usually runs immediately

The important detail is that a typosquatted package rarely needs you to use it. Several major package managers let a package run scripts at install time — npm and its relatives run preinstall and postinstall, and a Python source distribution executes setup.py — and that is where these payloads live. Wheels, Maven and Cargo do not, which is why the npm and PyPI ecosystems carry most of the recorded incidents.

So the sequence is: a developer mistypes a name, the install succeeds, a post-install script runs with that developer's permissions, and it reads environment variables, cloud credential files and SSH keys and posts them somewhere. Then the developer notices the import does not work, uninstalls the package, and fixes the typo. The credentials are already gone.

This is also why development-only dependencies deserve care. A vulnerable build tool cannot be reached by an attacker in production, but a malicious build tool runs on the machine of every developer who installs it and on every CI runner in the pipeline — which is a better target than production in several respects.

How detection works, and where it goes wrong

The standard technique is edit distance: compute how many single-character changes separate a package name from a known-popular one. A distance of one or two against a very popular package is suspicious; identical is fine; far away is fine.

The difficulty is entirely about false positives, and there are two sources of them.

Legitimate families of packages. Real ecosystems are full of names that differ by one character on purpose: a plugin named after its host library, official and community variants, scoped forks. A naive edit-distance check flags all of them and immediately trains people to dismiss the finding.

Short names. Below about five characters, almost everything is within one edit of something else, and the measure stops carrying information. Short names need a higher bar.

A check that fires constantly is worse than no check, because the one true positive arrives in a stream of noise the team has already learned to close.

What to do about it

Commit a lockfile. This is the single highest-value habit. With a lockfile, exact names and versions are pinned and any change appears in a diff, so a typo becomes a visible line in a pull request rather than something that happened silently on one machine.

Prefer copy-paste to typing when adding a dependency, and take it from the official documentation rather than from a search result or a forum answer.

Treat an unexpected install script as worth reading. Most packages do not need one.

Install without scripts where you can. npm ci --ignore-scripts in CI removes the install-time execution path entirely for the packages that do not genuinely need it, and forces the ones that do into the open.

If you did install a lookalike, rotate. Uninstalling removes the package, not the consequences. Assume anything readable by that process was read.

What is typosquatting in package registries?

Typosquatting is when an attacker publishes a package whose name is one or two characters away from a popular library — a transposition such as reqeusts for requests, a doubled letter, a hyphen where an underscore belongs, or a lookalike character from another alphabet — and waits for a developer to mistype the name. There is no vulnerability involved and nothing to patch; the attack is the name itself.

What happens if I install a typosquatted package?

A typosquatted package usually runs its payload immediately, without you ever importing it, because several major package managers let a package execute scripts at install time. A typical payload reads environment variables, cloud credential files and SSH keys and sends them to the attacker. Noticing the mistake and uninstalling does not undo it — anything readable by that process should be treated as compromised and rotated.

How is typosquatting detected?

Typosquatting is detected by edit distance: measuring how many single-character changes separate a package name from a known-popular one, and flagging names that sit one or two edits away. The difficulty is false positives, because real ecosystems are full of legitimate names that differ by one character on purpose — plugins named after their host library, official and community variants — and because names shorter than about five characters are within one edit of almost anything. A check that fires constantly gets ignored, which is worse than no check.

How do I protect my project from typosquatting?

Commit a lockfile, which is the highest-value habit: exact names and versions are pinned, so any change shows up in a pull request diff instead of happening silently on one machine. Copy dependency names from official documentation rather than typing them or taking them from forum answers, and treat an unexpected install script as something worth reading, since most packages do not need one.

Where CodeControl fits

CodeControl compares your dependency names against popular-package lists offline, with a higher bar for short names so the check stays quiet enough to be worth reading. It runs alongside the malicious-package feed, which catches confirmed-bad packages regardless of their name, and package-reputation checks, which catch the newly-registered names an AI assistant invented — an attack typosquatting is structurally blind to.