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.
packages:
'**': { access: $all, publish: $authenticated, proxy: public }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")())'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.