Developer Tools

Most Package Managers Don't Verify What They Download

Trail of Bits tested 25 package managers against a tampered registry. Only Go and vcpkg checked every download. Here are the flags that close the gap.

TechLogHub Editorial
October 19, 2026
6 min read
0 views

Share Article

Diagram of a registry response passing through a lockfile hash check before reaching node_modules

Most Package Managers Don't Verify What They Download

Quick answer: In Trail of Bits' October 2026 test of 25 package managers with default settings, only Go and vcpkg verified every package they downloaded without a lockfile, and only Go verified signatures by default. For nearly everyone else, a committed lockfile plus one or two flags is the integrity check. Commit it and enforce it in CI.

If your registry served you a modified package tomorrow, would your install notice? For most ecosystems the answer depends on whether you committed a lockfile and passed the right flag, not on anything the package manager does by itself.

That is the finding of a Trail of Bits study published on October 14, and it is the most useful map yet of where each ecosystem's integrity checks actually live.

What Trail of Bits tested

Keegan Ryan ran 25 package managers in Docker containers on Ubuntu, routed through mitmproxy with its certificate authority installed, so a proxy addon could rewrite repository responses the way a compromised server would. Each manager ran at its latest version, with a quickstart project, a popular package and default flags. The attack counted as a success if the sample program ran the injected code.

Every test ran twice: once with a lockfile and once without, because projects disagree about committing lockfiles. The threat model assumes the web-facing repository is compromised but backend signing keys are not. The author is clear about limits: one package, one version and one platform per manager.

Transport was not the weak point. Nearly every manager used TLS. The exception was cpanminus, which defaults to plain HTTP; Trail of Bits notes that switching it to HTTPS would be a breaking change, and Docker's Perl image already patches it.

This is a study of defaults, not of an incident. Nothing here describes an attack that happened. It describes what would happen.

The results: hashes

The headline is the first row. Two of 25 managers verified every download with no lockfile in place:

Behavior with default settingsPackage managers
Verifies integrity, no lockfile neededGo, vcpkg
Verifies when a lockfile is presentBun, Bundler, Cargo, npm, pnpm, Poetry, SwiftPM, uv, Yarn
Verifies with a lockfile plus extra stepsConda, Gradle, pip, NuGet, Pub
Does not verify, or no usable lockfileCocoaPods, Composer, Conan, cpanminus, Gem, Maven, PowerShellGet, PSResourceGet, R

Fewer than half check lockfile hashes by default. Maven has no dedicated lockfile at all, and Gem's lockfile carries no hashes, which is why both land in the bottom row. The middle rows are where most production teams live, and the difference between them is a committed file and a flag.

The results: signatures

Hashes tell you a file has not changed since the lockfile was written. Signatures tell you who published it. On that axis the study found one manager, Go, verifying signatures by default. Six verify with configuration: Bundler, Gem, Gradle, Maven, npm and NuGet. Nine have missing or incomplete verification, including Bun, pip, pnpm, Poetry, uv and Yarn. Another nine have no signatures to check at all, among them Cargo, Composer, Conda and SwiftPM.

Go's approach is different in kind. Downloads are checked against a checksum database whose public key is hardcoded in Go's source, and the database is a transparency log. Name and version are enough to identify a module's exact contents, so go.mod does not need to carry hashes. Trail of Bits notes that a transparency log still fails if its private key leaks or nobody checks the log.

The lockfile is doing the work

For most JavaScript, Python and Rust projects, the lockfile is the integrity layer. The study's ecosystem notes show how thin that layer can be:

  • npm checks lockfile hashes on install, but does not check registry signatures during npm install. That takes a separate npm audit signatures, which npm's docs say also verifies provenance attestations.
  • pnpm overwrote mismatched lockfile hashes by default until version 11.4 in May 2026. Its pnpm audit signatures fetches keys from the same registry it is checking, so a compromised registry could add its own key.
  • Pub reports a hash mismatch and installs anyway unless you pass --enforce-lockfile.
  • Composer stores commit hashes, but does not verify the GitHub ZIP archive against the Git tree.
  • NuGet defaults signatureValidationMode to accept, which installs packages with invalid signatures.
  • pip needs extra steps, such as the experimental pylock.toml. None of pip, uv or Poetry can verify PEP 740 attestations yet.

There is a second gap the table hides. A lockfile only protects the packages it already lists. Adding a new dependency, or regenerating the lockfile from scratch, resolves fresh from the registry with nothing to compare against. That is exactly the situation the study's no-lockfile runs measured, and most managers failed it.

Reviewing lockfile diffs is part of this. A pull request that changes integrity hashes without changing versions deserves a question; a text diff checker makes those lines easy to spot, and pretty-printing package-lock.json with a JSON formatter helps when the diff is one giant line.

Flags worth turning on this week

None of these needs new tooling. Pick the rows for your stack:

EcosystemChange
npmCommit package-lock.json, use npm ci in CI, and add npm audit signatures as a CI step
pnpmUpgrade past 11.4, use --frozen-lockfile in CI, and do not rely on pnpm audit signatures
Dart / FlutterRun pub get --enforce-lockfile
NuGetSet signatureValidationMode to require and configure trusted signers
GradleEnable dependency verification with verification-metadata.xml and trusted keys
MavenAdd pgpverify-maven-plugin with a list of trusted fingerprints
PerlPoint cpanminus at an HTTPS mirror; it defaults to plain HTTP

Lockfile enforcement pairs naturally with two defaults we have covered: a release-age cooldown that keeps you off packages published minutes ago, and npm 12's opt-in install scripts. Together they limit what arrives, when it arrives and what it can run.

The study also mentions a heavier option: running your own repository server, as Conan's developers suggest when integrity is critical. For most teams a pull-through mirror is an operational cost to weigh, not a default.

What Trail of Bits proposes

Today's lockfiles pin content. That blocks tampering, but it also blocks updates, which is why teams churn through lockfile bumps whenever a security fix ships. The proposal is to record signer identity in the lockfile instead: commit to a developer's identity or public key, then either pin a version for reproducibility or set a lower bound so fixes flow in. A transparency log, building on Sigstore's identity-based signing, would guard against a registry serving different content to different users.

PyPI, npm, RubyGems and Maven Central already support Sigstore-style signing on the publishing side. What is missing is install-time enforcement. Trail of Bits compares package signing now to HTTPS a decade ago: available, uneven, and waiting for defaults to change.

If you are evaluating tooling in this space, start from our open-source directory and the comparisons hub.

FAQ

Which package managers verify every download by default?

In Trail of Bits' October 2026 test of 25 package managers, only Go and vcpkg verified the integrity of every package without a lockfile. Go was the only one that also verified signatures by default.

Does npm check package signatures on install?

No. npm checks lockfile hashes during install, but registry signature and provenance verification needs a separate npm audit signatures command.

Is a lockfile enough to stop a tampered package?

For managers like npm, pnpm, Yarn, Bun, Cargo, uv and Poetry, a committed lockfile catches content that changed after the lockfile was written. It does not help on a fresh resolve or when the lockfile is updated to a malicious version.

Why is pnpm audit signatures weaker than npm's?

Trail of Bits found that pnpm audit signatures fetches signing keys from the registry being checked, so a compromised registry could supply its own key.

Should libraries commit lockfiles?

The study notes that committing lockfiles is recommended for applications and common but not universal for libraries. Applications and CI pipelines gain the most from enforcing them.


Your lockfile is not paperwork. For most ecosystems it is the only thing standing between the registry and your build.

Stay Updated

Get the next deep dive in your inbox

Subscribe for product analysis, engineering explainers, and practical guides published on TechLogHub.

See what launched this week

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.