Skip to content

fix(typosquat): suppress scoped-package and short-name false positives (v0.5.5)#8

Merged
pkuzco merged 1 commit into
mainfrom
fix/typosquat-false-positives
May 27, 2026
Merged

fix(typosquat): suppress scoped-package and short-name false positives (v0.5.5)#8
pkuzco merged 1 commit into
mainfrom
fix/typosquat-false-positives

Conversation

@pkuzco

@pkuzco pkuzco commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

A real-world scan against an Astro/Wix project surfaced 34 typosquat false positives. Three patterns accounted for all of them, fixed in this PR:

1. Scoped packages compared against unscoped popular targets (15 FPs). The checker stripped the scope and compared the basename, so @babel/parser was flagged as a possible typosquat of parcel, @astrojs/prism of prisma, @floating-ui/core of ora, etc. But npm scopes carry verified ownership — @babel/parser is published by the babel team and cannot impersonate parcel. Skip the check entirely for scoped names. Detecting real scope-confusion attacks (@bable/lodash) needs per-scope reputation data we don't have; that's an explicit tradeoff documented in the code.

2. MAX_DISTANCE = 2 too lenient for short names (16 FPs). Distance 2 across 4-5 char names means ~half the characters differ — that's a different word, not a typo. Examples: asap/tsup, clsx/tsx, vfile/vite, regex/remix, defu/debug, nise/nest, jose/core, parse5/parcel. Switched to a length-adaptive threshold: require distance 1 when either name is shorter than 7 chars. Distance-1 short typos like lodsh/lodash are still caught.

3. Legit packages at distance 1 from popular targets (3 FPs). Allowlisted color, acorn, react-dnd, jose, prismjs — all widely-used real packages that sit at distance 1 from existing entries (colors, react-dom, @nestjs/core, etc.).

Of the 34 FPs in the original report, 31 disappear with these fixes. Remaining edge case: piccolore vs picocolors (genuinely suspicious-looking; kept flagged).

Test plan

  • All 102 tests pass (npm test)
  • Existing typosquat tests still cover lodahs/lodash (transposition), expres/express (deletion), cornmander/commander (homoglyph), reqests/requests (PyPI)
  • New tests cover scoped-exemption and short-name behavior
  • npm run build clean
  • Manual scan of the original reproducer project (Astro/Wix) — expect ~3 typosquat flags instead of 34

🤖 Generated with Claude Code

…s (v0.5.5)

Scoped npm packages have verified scope ownership: @babel/parser is
published by the babel team, it cannot pretend to be `parcel`. Comparing
scoped basenames against unscoped popular targets was producing
overwhelming false positives across @astrojs/*, @babel/*, @floating-ui/*,
@img/*, @react-dnd/*, @shikijs/*, @sinonjs/*, @types/*, @wix/* etc. Skip
the check for scoped names; detecting real scope-confusion attacks
(@bable/lodash) needs per-scope reputation data we don't have.

Distance 2 across 4-5 char names means ~half the characters differ —
that's a different word, not a typo. Tighten the threshold to require
distance 1 when either name is shorter than 7 chars. This eliminates
asap/tsup, clsx/tsx, vfile/vite, regex/remix, defu/debug, nise/nest,
jose/core, parse5/parcel and similar legitimately-distinct pairs while
still catching real short-name typos like lodsh/lodash.

Allowlist five legit packages that sit at distance 1 from popular
targets and were being flagged on first scan: color, acorn, react-dnd,
jose, prismjs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@pkuzco pkuzco merged commit 43287e0 into main May 27, 2026
2 checks passed
@pkuzco pkuzco deleted the fix/typosquat-false-positives branch May 27, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant