caon.io

Dependency Confusion

An internal package name that is not registered on the public registry. Publish it there with a higher version and the build pulls yours.

Harvest package names

# frontend bundles and lockfiles
curl -s https://example.com/main.js | grep -oE '"[a-z0-9@/._-]+":\s*"\^?[0-9]' | sort -u

# on GitHub
package.json  package-lock.json  yarn.lock  .npmrc
requirements.txt  Pipfile  setup.py  pyproject.toml
pom.xml  build.gradle  composer.json  Gemfile  go.mod

Also check exposed .git, source maps, Docker images, and CI logs.

Check the public registry

npm view @scope/internal-lib          # 404 = candidate
pip index versions internal-lib
curl -s https://registry.npmjs.org/internal-lib | jq .error

Scoped npm packages (@company/thing) are only confusable if the scope itself is unregistered.

Before publishing anything

Publishing a package that a real build might install is a live action against production. Only do it when the program’s policy allows it, and:

  • Give it a version that is plausible, not 99.99.99, so you don’t break the build
  • No install hooks, no code execution — DNS callback only
  • Name yourself in the description and link the report
  • Unpublish as soon as it is triaged

Most programs accept the unregistered-name evidence on its own. Lead with that.

Non-destructive proof

{
  "name": "internal-lib",
  "version": "0.0.1-security",
  "description": "Reserved by <handle> for report #12345. No functionality.",
  "scripts": {}
}

0.0.1-security is the convention for a defensive placeholder — it will not win a ^1.2.3 resolution, so it proves the name was free without hijacking a build.

  • Typosquatreqeusts, python-dateutil vs dateutil
  • Repo-jacking — a go.mod / package.json pointing at a renamed or deleted GitHub user
  • Namespace reuse — a deleted org name someone can re-register
# every GitHub org/user a go.mod depends on
grep -oE 'github\.com/[A-Za-z0-9_.-]+' go.mod | sort -u
# then check which 404

Defence to check for in the report

  • .npmrc scope-to-registry mapping
  • --index-url without --extra-index-url for pip
  • Artifactory / Nexus in “local first” mode
  • Lockfiles committed and CI running npm ci, not npm install

Articles

↑↓ navigate↵ openesc close