{"post":{"slug":"dependency-confusion-attacks-2026","title":"Dependency Confusion: When Your Build Installs the Wrong Package","excerpt":"Your internal package is not on the public registry. Someone publishes one with the same name, your package manager prefers the higher version, and their code runs in your build.","category":"code","date":"February 11, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">Y</span>our company has an internal package called `acme-shared-utils`, published to a private registry, at version 1.4.2. It is not on the public registry.\n\nSomeone publishes `acme-shared-utils` version 99.0.0 to the public registry. Your package manager, configured to check both, sees a higher version publicly and installs it. Their code now runs in your build with whatever access your build has.\n\nThe attack requires no compromise of your systems. It requires knowing an internal package name, which frequently leaks through public code, error messages, or job listings.\n\n## What changed in 2026\n\n- **Namespace reservation became standard practice.** Reserving an organisation namespace on public registries became routine.\n- **Registry configuration improved.** More package managers gained explicit controls for which registry serves which package.\n- **Lockfile integrity verification spread.** Cryptographic hashes in lockfiles became widely enforced rather than optional.\n- **Install-script scrutiny increased.** Restricting or auditing lifecycle scripts became a common policy.\n\n## Why version resolution causes it\n\nThe root cause is that many package managers resolve a name across all configured registries and select by version rather than by source.\n\nThat is reasonable default behaviour for public packages and dangerous when the same name exists in two places. An attacker who publishes a very high version wins the resolution.\n\n| Configuration | Vulnerable? |\n| --- | --- |\n| Checks public and private, picks highest version | Yes |\n| Private registry with fallback to public on miss | Yes, if the name is missing internally |\n| Explicit mapping of names to registries | No |\n| Reserved namespace on the public registry | No |\n| Lockfile with pinned source and hash | No, for existing dependencies |\n\nThe second row catches people who believe a private registry protects them. If the private registry returns a miss — a typo, a package not yet published, a transient failure — and the configuration falls back to public, the substitution happens.\n\n## Namespaces are the strongest defence\n\nPublishing internal packages under a namespace your organisation controls on the public registry means nobody else can publish under it. The name simply cannot be squatted.\n\nWhere the ecosystem supports scoping, this is the cleanest structural fix: reserve the namespace publicly, publish internal packages under it privately, and the ambiguity disappears.\n\nWhere scoping is unavailable, the equivalent is **explicit registry mapping** — configuring which registry serves which name prefix, so internal names are never resolved publicly regardless of what exists there.\n\nSome organisations also publish placeholder packages publicly under their internal names, occupying the namespace defensively. Crude and effective where scoping is not an option.\n\n## Lockfiles and integrity\n\nA lockfile records exactly which version came from which source, with a cryptographic hash of its contents.\n\nThat protects existing dependencies: if a package's contents change, or a different package is substituted, the hash mismatches and installation fails. This is the mechanism that stops silent substitution.\n\nTwo requirements. The lockfile must be **committed** and used — an install that ignores it or regenerates it defeats the protection. And builds should use the strict install mode that fails on mismatch rather than resolving fresh.\n\nThe gap the lockfile does not cover is a *new* dependency being added. Its first resolution has nothing to compare against, which is where namespaces and registry mapping matter.\n\n## Install scripts\n\nWorth stating explicitly because it explains the urgency: many ecosystems allow packages to run scripts at install time, before any of your code executes and before tests run.\n\nSo a substituted package does not need you to import it. Installing it is enough, and it runs with whatever access your build environment has — which frequently includes credentials, cloud access, and the ability to modify build artefacts.\n\nRestricting or disabling lifecycle scripts, where your workflow tolerates it, removes a large part of the attack surface. Where it does not, auditing which dependencies use them narrows the concern to a reviewable set.\n\n## Common mistakes\n\n- **Assuming a private registry is sufficient.** Fallback behaviour matters.\n- **No namespace reservation.** The name stays available to anyone.\n- **Lockfile not committed or not enforced.** No integrity check.\n- **Install mode that resolves fresh.** Ignores the lockfile.\n- **Internal package names in public code.** Discloses the target.\n- **Install scripts unrestricted.** Compromise before execution.\n- **No alert on dependency source changes.** Substitution goes unnoticed.\n\n## FAQ\n\n**How would I know if this happened?**\nA dependency resolving from an unexpected registry, an unexpected version jump, or an integrity mismatch. Without lockfile enforcement and source logging, quite possibly you would not.\n\n**Does this affect internal-only repositories?**\nYes — the attack targets the build, not the repository. Any build resolving package names across registries is exposed.\n\n**What about transitive dependencies?**\nSame risk, less visibility. A dependency of a dependency referencing an internal name has the same exposure, which is an argument for registry-level controls rather than per-project configuration.\n\n**Is this the same as typosquatting?**\nRelated. Typosquatting relies on a developer mistyping a name; dependency confusion relies on resolution preferring a public package with a name you already use correctly.\n\n## Where to go next\n\nFor the build caching that must trust its inputs, read [incremental builds](/blog/incremental-builds-explained-2026). For the wider pipeline, [CI/CD best practices](/blog/ci-cd-best-practices-2026), and for verifying what you ship, [API fuzzing](/blog/api-fuzzing-explained-2026).","hue":306,"metaTitle":"Dependency Confusion: Builds Installing the Wrong Package","metaDescription":"How dependency confusion substitutes public packages for internal ones, why version resolution causes it, and the namespace and lockfile defences.","keywords":["dependency confusion","supply chain attack","package substitution","private registry","namespace squatting","lockfile integrity","install scripts","software security 2026"],"tldr":["**Package managers checking several registries may prefer the public one** — usually by choosing the highest version.","**Scoped or namespaced names are the strongest defence** — a reserved namespace cannot be squatted.","**Pin to a lockfile and verify integrity hashes** — the lockfile is what stops silent substitution.","**Install scripts run at install time** — compromise happens before any of your code executes.","**Skip:** relying on a private registry alone if it falls back to public on a miss."],"readTime":"4 min read","wordCount":876},"related":[{"slug":"dependency-pinning-strategy-2026","title":"Dependency Pinning: Between Frozen and Broken","excerpt":"Pin everything and you never get security fixes; pin nothing and builds break randomly. Here is a strategy that gives reproducibility without freezing in place.","category":"code","date":"June 26, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":140},{"slug":"sbom-supply-chain-security-2026","title":"SBOM and Supply Chain Security: Beyond the Checkbox","excerpt":"A software bill of materials lists what is in your build. Here is how to generate one that is actually useful, and why most SBOM programmes stop short of value.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":6},{"slug":"what-is-a-dependency-2026","title":"What Is a Dependency in 2026: code your project relies on","excerpt":"A dependency is external code your project relies on to work, like a library you install instead of writing yourself. Here is what a dependency is and how to manage it.","category":"code","date":"June 9, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":240}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"best-vpn-services-2026","title":"Best VPN services in 2026: real privacy, streaming, and price compared","excerpt":"VPNs are still useful in 2026 — but the right one depends on whether you want real privacy, geo-unblocking, or both. Here is the honest comparison across the four that survive scrutiny.","category":"tech","date":"February 11, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":250},"older":{"slug":"estate-tax-portability-2026","title":"Estate Tax Portability: Not Losing the First Spouse's Exemption","excerpt":"When the first spouse dies, their unused estate tax exemption can transfer to the survivor — but only if someone files a return to elect it, which frequently nobody does.","category":"finance","date":"February 11, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":293}}