Safeguard
Open Source Security

What is a Package Manager

Package managers like npm and pip automate dependency resolution — and have been the entry point for incidents from event-stream to the xz-utils backdoor.

James
Principal Security Architect
7 min read

A package manager is a tool that automates installing, updating, configuring, and removing software dependencies for a codebase. When a developer runs npm install express or pip install requests, the package manager resolves version constraints, downloads the requested code and its transitive dependencies from a registry, verifies checksums, and writes the result into a dependency tree the build can use. npm (JavaScript), pip (Python), Maven and Gradle (Java), Cargo (Rust), NuGet (.NET), Composer (PHP), and Go modules are the dominant examples, collectively resolving billions of downloads a week. The npm registry alone hosts more than 2.1 million packages and served over 3 trillion downloads in 2023. Because a single install command can pull in hundreds of packages a developer never directly reviewed, package managers are also one of the highest-leverage attack surfaces in modern software — a fact attackers have exploited repeatedly over the past decade.

What Does a Package Manager Actually Do?

A package manager's core job is dependency resolution: matching version ranges (like ^4.17.1) declared in a manifest file against what's actually available in a registry, then fetching a consistent, non-conflicting set of packages. It reads a manifest (package.json, requirements.txt, pom.xml, Cargo.toml), consults a registry (npm registry, PyPI, Maven Central, crates.io) over HTTPS, and writes a lockfile (package-lock.json, poetry.lock, Cargo.lock) that pins exact resolved versions and hashes so builds are reproducible. Most package managers also expose lifecycle hooks — npm's postinstall, for instance — that run arbitrary scripts automatically during installation, which is precisely the mechanism attackers abuse to execute malicious code the moment a package is pulled down.

Which Package Managers Do Most Developers Use?

The most widely used package managers map directly to the dominant language ecosystems: npm and Yarn for JavaScript/Node.js (2.1M+ packages), pip and Poetry for Python (over 550,000 projects on PyPI as of 2024), Maven and Gradle for Java (Maven Central hosts roughly 650,000 artifacts), RubyGems for Ruby (over 185,000 gems), Cargo for Rust (over 150,000 crates), Composer for PHP, and Go's built-in module system, which resolves packages directly from source-control hosts like GitHub rather than a centralized registry. A typical mid-sized web application built on npm pulls in 80-150 transitive dependencies for every one direct dependency the developer chose, according to dependency-graph analyses published by Snyk and Sonatype — meaning the actual attack surface is an order of magnitude larger than what shows up in a package.json.

How Does a Package Manager Resolve Transitive Dependencies?

A package manager resolves transitive dependencies by recursively walking the dependency graph: your direct dependency declares its own dependencies, those declare more, and the package manager flattens or nests the result into a single resolvable tree using semantic versioning (semver) rules. This is why one direct install can silently introduce dozens of indirect ones — Sonatype's 2023 State of the Software Supply Chain report found that 96% of vulnerable open source downloads had a non-vulnerable version available, meaning most exposure comes not from missing patches but from resolution choices buried several layers deep in a tree no human reviewed. Lockfiles freeze that resolution to specific hashes, which prevents "dependency drift" but does nothing to stop a legitimate-looking new version from shipping malicious code the first time it's pinned.

What Security Incidents Have Come Directly From Package Managers?

Package manager compromises have caused some of the most damaging supply chain incidents on record, because malicious code inserted into a popular package gets distributed automatically to every downstream project on the next install. In November 2018, the npm package event-stream (nearly 2 million weekly downloads) was hijacked when its original maintainer handed control to an attacker who added a dependency, flatmap-stream, containing code that targeted the Copay Bitcoin wallet to steal private keys. In October 2021, ua-parser-js — used by over 1,000 dependent packages including Facebook and Amazon tooling — was compromised for roughly four hours, pushing versions that installed a cryptocurrency miner and a Windows/Linux password stealer. In March 2022, the maintainer of node-ipc added "protestware" that wiped files on machines with Russian or Belarusian IP addresses, affecting downstream users of the popular vue-cli toolchain. In January 2022, the maintainer of colors.js and faker.js (used by thousands of projects, including Amazon's AWS CDK) intentionally sabotaged his own packages, printing garbled text and infinite loops to protest unpaid open source labor. And in March 2024, security engineer Andres Freund discovered CVE-2024-3094, a backdoor deliberately planted in xz-utils versions 5.6.0 and 5.6.1 over roughly two years by a trusted co-maintainer, designed to give attackers SSH access to affected Linux systems — caught only because Freund noticed a 500-millisecond SSH login delay during unrelated performance debugging.

How Are Package Manager Attacks Different From Ordinary CVEs?

Package manager attacks differ from ordinary CVEs because they're frequently intentional and undisclosed rather than accidental and patched. A traditional vulnerability — say, a buffer overflow — gets assigned a CVE, a fix ships, and scanners flag any version below the patched release. A malicious package publish has no CVE at the moment it happens; it looks like a normal version bump, passes through the same trust chain as every legitimate update, and often executes via an install-time script before any code review occurs. This is why traditional CVE-database scanning catches roughly zero percent of incidents like event-stream or ua-parser-js at the time of compromise — those packages had no known vulnerabilities in the CVE sense, only malicious intent that surfaced hours or years later. Typosquatting compounds the problem: researchers have repeatedly found hundreds of squatted package names on PyPI and npm (variants like reqeusts or crossenv) mimicking popular libraries to catch developer typos during install.

How Can Security Teams Reduce Package Manager Risk?

Security teams reduce package manager risk by combining lockfile enforcement, provenance verification, and continuous monitoring rather than relying on install-time trust alone. Concrete steps include pinning dependencies to exact versions and commit hashes in lockfiles, enabling npm's provenance attestations or Sigstore/cosign signing to verify a package was built from the source it claims, restricting install scripts (npm install --ignore-scripts) in CI pipelines, and maintaining a private registry proxy (Artifactory, Nexus, or Verdaccio) that mirrors and vets packages before they reach developer machines. Generating and maintaining a Software Bill of Materials (SBOM) in CycloneDX or SPDX format gives teams a queryable inventory of every transitive dependency, which is what let organizations running an SBOM practice identify their xz-utils exposure within hours of the March 2024 disclosure rather than days.

How Safeguard Helps

Safeguard turns package manager risk from a guessing game into a measurable, prioritized workflow. Our platform ingests and generates SBOMs across npm, PyPI, Maven, Cargo, and Go module ecosystems, then uses reachability analysis to determine whether a vulnerable or newly flagged package is actually invoked by your application's code paths — not just present in a lockfile. Griffin, Safeguard's AI security analyst, continuously correlates registry-level signals (new maintainer activity, suspicious install scripts, anomalous version publishes) against your dependency graph to surface incidents like event-stream or xz-utils before they become headlines. When a fix is available, Safeguard opens auto-fix pull requests that bump the affected package to a safe, compatible version with the reachability context attached, so engineering teams can merge with confidence instead of triaging blind. The result is fewer false positives, faster remediation, and a package manager attack surface your team can actually see.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.