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.modAlso 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 .errorScoped 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.
Related
- Typosquat —
reqeusts,python-dateutilvsdateutil - Repo-jacking — a
go.mod/package.jsonpointing 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 404Defence to check for in the report
.npmrcscope-to-registry mapping--index-urlwithout--extra-index-urlfor pip- Artifactory / Nexus in “local first” mode
- Lockfiles committed and CI running
npm ci, notnpm install