A build of an internal library suddenly took minutes instead of seconds and began making unexpected outbound connections. All dependencies were internal and the component had no reason to reach the internet.
Initial suspicion fell on the proxy repository cache, yet its configuration was correct. Investigation revealed that the package manager was querying the company’s own package not only from the internal proxy but also from the public registry because a second source had been added to the settings.
Internal libraries are named with ordinary strings such as billing-common or auth-client. In most ecosystems a package is identified solely by this string; the first party to register the name on the public registry owns it. Because these human-readable names are rarely claimed publicly, they remain available for anyone to take.
When a package with the internal name and a deliberately high version such as 99.0.0 is published, the build system selects it over the legitimate internal version. Alex Birsan proved the attack in February 2021 by collecting internal names from public repositories and error logs, publishing high-version packages under those names, and waiting. The packages reached builds at more than 35 large organizations, including Microsoft, Apple, PayPal, Shopify, Netflix, Tesla and Uber.
Execution occurs at install time. In Python, setup.py runs when a source distribution is built; in npm, lifecycle scripts execute by default. Both run with the build agent’s privileges, granting access to tokens, signing keys and the internal network even if the resulting artifact is never promoted to production.
Effective defenses include using a single internal proxy repository that never mixes public answers for internal names, registering scoped namespaces such as @company and binding them to the private registry, maintaining lock files with content hashes, and disabling install scripts with flags such as --ignore-scripts.
