Dependency Confusion and Typosquatting: Practical Defense for Dev Teams
How registry policies, lockfiles and scoping block malicious packages before they hit the build. Hands-on technical guide from the Basilisk team.
Back in 2021 a researcher earned over 130 thousand dollars in bug bounties by publishing packages with internal names from Apple, Microsoft, PayPal and dozens of other companies on public npm and PyPI. The attack used zero zero-days: it simply exploited the precedence package managers give to the public registry when a name exists on both sides. Five years later, Basilisk teams still find this vector wide open in 7 out of 10 audited pipelines. Dependency confusion and typosquatting are not academic curiosities, they are the cheap front door to compromise an entire build and, by extension, every customer who receives that artifact.
The dependency confusion mechanism is simple and brutal. You have an internal package called acme-payments-sdk hosted on a private Nexus or Artifactory. An attacker discovers that name in a leaked package.json, a Stack Overflow post or a public docker layer, and publishes acme-payments-sdk@99.0.0 on npmjs.com. Next time the pipeline runs npm install without a strict lockfile, the resolver picks the higher version. Done, arbitrary code running inside your CI with AWS credentials, GitHub tokens and access to the internal registry. We saw this in the field during assessments similar to REST and GraphQL API Pentest: Technical Checklist for Legal Bug Bounty, where the build surface was more exploitable than the API itself.
Typosquatting plays in another league. Instead of taking over the real name, the attacker registers colorss, requets, python-dateutill, lodahs. Phylum research in 2024 cataloged more than 11 thousand malicious packages on PyPI using this pattern in twelve months. Payloads vary: env variable theft, mining, RAT installation, DNS exfiltration. The first defense is cultural: mandatory code review on any dependency change. Tools like Socket, Snyk Advisor and deps.dev already flag freshly published packages, packages without maintainer history or with suspicious install script patterns.
Lockfiles solve 80 percent of the problem if you use them seriously. package-lock.json, yarn.lock, pnpm-lock.yaml, poetry.lock, Cargo.lock and go.sum pin not just the version but the hash. Configure npm ci, pnpm install --frozen-lockfile, pip install --require-hashes and cargo --locked in pipelines. No loose npm install in production. Combined with an .npmrc setting registry=https://nexus.internal/repository/npm-group/ and always-auth=true, you drastically reduce the chance the resolver wanders to the public registry by accident. The mental pattern echoes what we covered in Supply Chain Security: Sigstore Signing and Real SBOMs in CI/CD about SBOMs and attestations.
Scoping is the definitive weapon against dependency confusion in JavaScript. Migrate every internal package to @acme/payments-sdk, @acme/auth, @acme/billing. In .npmrc, set @acme:registry=https://nexus.internal/. Now npm only resolves that scope on the internal registry, even if someone publishes @acme/payments-sdk on npmjs (Microsoft has been reserving common scopes since 2021). In Python, use explicit separate indexes with pip --index-url for internals and --extra-index-url only when needed, or better, mirror everything via devpi or Artifactory. For Go, GOPRIVATE=git.acme.com blocks queries to proxy.golang.org. This hardening connects directly to Linux Server Hardening: Applying CIS Benchmark Without Breaking Production on the least privilege principle.
On the CI side, isolate. Every build job should run with ephemeral tokens, no production access, network egress filtered by a proxy allowing only the internal registry, github.com and known endpoints. OIDC with short-lived credentials on GitHub Actions or GitLab CI removes long-lived secrets. Add a pre-install verification step: scripts/check-deps.sh running npm audit signatures, validating that no dependency was published in the last 7 days without manual approval, and checking packages against an allowlist. Datadog published a 2025 report showing 62 percent of supply chain compromises would have been blocked by these three rules combined.
Continuous monitoring closes the loop. Configure Sigstore Rekor alerts for any publication carrying your organization name, register typo-squatting domains of your product (acme-cloud, acme-coud, acmecloud), and keep an inventory of cosign-signed SBOMs for every release. When a suspicious package shows up you already have evidence for takedown and data for incident response, in the style we showed in DFIR on Linux: Live Triage with UAC and Velociraptor. Train the team to report any new install script warning instead of reflexively ignoring it. Practical takeaway: today, audit your .npmrc, .pip.conf and go env. If you cannot answer in 30 seconds which registry each dependency comes from, you are already vulnerable.