typosquat
A typosquat checker for a dependency list. It reads the package.json of a
project, collects every dependency name from dependencies,
devDependencies, peerDependencies and optionalDependencies, and
compares each name against a bundled list of popular npm package names.
A dependency that is not itself a popular package but closely resembles
one is flagged as a possible typosquat, with the popular package it
resembles named as evidence.
The check is fully offline and deterministic: no network calls, no
timestamps, findings sorted by dependency name.
Install
Or install it globally:
CLI
check [path]: check the project at path (a directory containing a
package.json, or a direct path to a package.json file). Defaults to
the current directory. Add --json for the machine readable report.
mcp: run the MCP server over stdio.
--version: print the version.
Exit codes: 0 when the verdict is CLEAR, 1 when the verdict is
SUSPECT, 2 for usage errors and for a missing or invalid package.json.
Example human output:
MCP
The MCP server exposes one tool:
check_typosquat with a path argument. It returns the same report as
the CLI --json output: tool name, version, target, verdict, counts
(depsChecked, flagged), findings and a summary.
Matching rules
Names are normalized by lowercasing before comparison. A dependency that
is itself in the popular list is never flagged. For every other
dependency, the strongest matching rule wins:
SEPARATOR-SQUAT (high severity): the dependency name equals a popular
name once hyphens, underscores and dots are stripped from both names,
but the raw names differ. Example: node_fetch vs node-fetch.
DISTANCE-1 (high severity): Levenshtein edit distance 1 from a
popular name. Example: axioss vs axios.
DISTANCE-2 (medium severity): Levenshtein edit distance 2 from a
popular name whose length is 9 or more characters. Short names are
excluded at distance 2 because too many unrelated short names sit
within two edits of each other. Example: typescrpit vs typescript.
Scope confusion is not checked: an unscoped dependency that equals only
the unscoped part of a scoped popular name (for example client vs
@prisma/client) is too weak a signal to flag.
Verdict
CLEAR: no dependency resembled a popular package name.
SUSPECT: at least one dependency was flagged.
Limits
- The popular package list is a snapshot dated 2026-10-25, written from
knowledge of the npm ecosystem. It will drift: new packages become
popular and old ones fade, and a snapshot cannot track that.
- Edit distance catches typos, not every squatting style. It does not
catch combosquats (a popular name combined with an extra word, such as
axios-helper), homoglyph or bit squats, or scope tricks beyond the
separator rule above.
- The checker reads names only. It does not inspect package contents,
maintainers, download counts or publish history, so it cannot judge
malicious intent.
- A flag is a resemblance, not an accusation. Legitimate packages can sit
one edit away from a popular name. Treat every finding as a prompt to
look, not as proof of wrongdoing.
License
MIT