Typosquatting

Typosquatting and Dependency Confusion

Typosquatting publishes malware under a name one keystroke from a popular one. In 2017 npm 2,036 removed 38 packages from one account, among them crossenv (the real package is cross-env), which sent the installing machine's environment variables to its author. PyPI 2,431 and crates.io see the same pattern; both at least treat - and _ in names as equal, so my-pkg and my_pkg cannot have different owners. Copy install commands from a project's own README, not from memory.

Dependency confusion, published by Alex Birsan in 2021 after it reached internal builds at Apple, Microsoft and more than 30 other companies, needs no typo. A company uses an internal, unscoped name such as booknest-internal-utils from a private registry that also proxies the public one. The attacker publishes the same name publicly with a higher version, and the resolver of Graphs and Semver Ranges does the rest. To reproduce it, this lab runs two more Verdaccio containers: l1-verdaccio-public plays npmjs, where the "attacker" published 1.99.0, and l1-verdaccio-naive (port 31875) holds the real 1.0.0 with the proxy-everything rule most private registries start with.

The package rule of ~/v5-ch1/supply/naive/conf/config.yamlShell
packages:
  '**': { access: $all, publish: $authenticated, proxy: public }
Installing an internal package through a proxying registryShell
cd ~/v5-ch1/supply/consumer && cat .npmrc
npm view booknest-internal-utils versions
NPM_TOKEN=npm_fake_demo npm install booknest-internal-utils@^1.0.0 --foreground-scripts \
  --no-audit --no-fund
node -e 'console.log(require("booknest-internal-utils")())'
Output
registry=http://localhost:31875/
[ '1.0.0', '1.99.0' ]
> booknest-internal-utils@1.99.0 postinstall
> node steal.js
[1.99.0 postinstall] user=dev cwd=/home/dev/v5-ch1/supply/consumer/node_modules/booknest-intern
  al-utils
[1.99.0 postinstall] ~/.npmrc readable: false; secret-like env vars: 1
added 1 package in 377ms
npm warn install-scripts 1 package has install scripts not yet covered by allowScripts:
npm warn install-scripts   booknest-internal-utils@1.99.0 (postinstall: node steal.js)
...
attacker 1.99.0

The registry merged both versions, ^1.0.0 accepted 1.99.0, and the attacker's postinstall ran; npm 11.19 only warned. The fix is Verdaccio's configuration: internal packages in a scope you own (@booknest/*) with no uplink, and the same scope registered on npmjs so nobody else can publish in it.