Skip to content

Back to the catalog

Package managers have an integrity problem
blog.trailofbits.comwriteup

Package managers have an integrity problem

Trail of Bits tested 25 popular package managers with a Docker and mitmproxy harness that simulates a compromised package repository injecting malware into legitimate packages. Fewer than half enforce lockfile hashes by default, only two verified every download, and only Go verifies signatures by default. The post covers lockfile hash pitfalls (Pub, pnpm, Conan, CocoaPods, Composer), repository, developer and Sigstore signing, and Go's checksum database. It also proposes lockfiles that commit to a signing identity plus a transparency log.

TL;DR Trail of Bits tests 25 package managers against a malicious repository: most lack default integrity checks, and only Go verifies signatures by default.
Document

What it is

Trail of Bits' test of 25 source-code package managers against a compromised package repository that injects malware into legitimate packages. The post tells you which tools detect tampering by default, which only do so with extra configuration, and which cannot detect it at all. The harness is Docker plus a mitmproxy addon. The mitmproxy CA is installed in the image, so the addon can rewrite repository responses. Backend signing keys are assumed to stay secure.

Where defaults fail

  • cpanminus uses plain HTTP by default, so any active network attacker can inject code. The official Perl Docker image patches it to use HTTPS. Its CHECKSUMS signature verification was removed after a bypass vulnerability.
  • Lockfile hash present but not enforced:
  • Pub installs on a hash mismatch unless --enforce-lockfile is passed. flutter run and flutter test call flutter pub get, so they can overwrite pubspec.lock unless called with --no-pub.
  • pnpm overwrote the lockfile after a mismatch until version 11.4 (May 2026). Before that, --frozen-lockfile was needed.
  • Lockfile hash not a content hash:
  • Conan locks an MD5 recipe revision that it never recomputes, and the platform binary's package revision is not in the lockfile.
  • CocoaPods locks a SHA-1 of the Podspec, not of the source fetched from GitHub.
  • Composer locks a commit hash, but the ZIP archive it downloads from GitHub is not verified against it.
  • No hashes or no lockfile:
  • Gradle's gradle.lockfile and Gem's Gemfile.lock have no hashes. Gradle's verification-metadata.xml does have content hashes.
  • Maven, R, PowerShellGet, PSResourceGet and cpanminus have no dedicated lockfile.
  • pip (experimental pylock.toml), Conda (optional Conda lock) and NuGet (needs RestorePackagesWithLockFile) require opt-in.
  • Hash-enforcing lockfiles by default: Bun, npm, pnpm, Yarn, uv, Poetry, Bundler, Cargo and SwiftPM. vcpkg ships hashes in its own source tree, which is secure as long as it is updated through git pull.

Signatures

Only Go verifies signatures by default. It checks module hashes against a checksum database (a transparency log) whose public key is hardcoded. This gives integrity with no hashes in go.mod. go.sum is only a local cache. The trade-off is that trust concentrates in one private key.

Everyone else needs configuration, or cannot verify at all:

ManagerStatus
npmnpm install skips signature checks; npm audit signatures checks them
pnpmpnpm audit signatures fetches the registry keys from the possibly malicious registry, so an attacker can swap in their own key
Bun, Yarnno equivalent command
NuGetdefault signatureValidationMode is accept; set it to require and configure trusted signers, e.g. dotnet nuget trust source
Gem, Bundleradd developer keys manually with gem cert --add
Maventhird-party pgpverify-maven-plugin with pgp-keys-map.list
Gradledependency verification
pip, uv, Poetrycannot verify PEP 740 attestations
PowerShellno Authenticode verification on non-Windows
Cargo, CocoaPods, Composer, Conan, Conda, Pub, R, SwiftPMno signatures to verify

What to do

  • Check which row your package manager falls in. Where verification is off by default, turn it on: --enforce-lockfile (Pub), --frozen-lockfile (pnpm), NuGet require mode with trusted signers, and Gradle dependency verification or the Maven plugin.
  • Commit lockfiles for applications. Libraries are less clear-cut. Many managers recommend committing for libraries (npm, pnpm, Poetry, uv, Yarn), Bundler recommends excluding, and Cargo leaves it to the developer.
  • Hash-pinned lockfiles block immediate security updates, because a new release has a new hash. This forces frequent lockfile bumps.
  • The proposed direction is for lockfiles to pin a signer identity or key plus a version or lower bound, backed by a transparency log, using Sigstore. pylock.toml, Gradle trusted-keys and pgp-keys-map.list already move this way.

Caveats

  • Each manager was tested with one package, one version (the latest) and one platform (Ubuntu), including PowerShell and CocoaPods. The result shows a developer following reasonable conventions can be vulnerable, not that everyone is.
  • System package managers (APT, Chocolatey, Homebrew) are out of scope.
  • The vcpkg result excludes the question of securely updating vcpkg itself.
  • SwiftPM was tested over Git. Its registry protocol supports signatures, but no public registry implements it.
  • PowerShell on Windows was not tested. The documented default ExecutionPolicy would achieve integrity there.
  • The hash-present-but-bypassable cases are documented behavior, not necessarily vulnerabilities.
  • Repository signing does not help if the repository itself legitimately signs malicious code.

Organisations named here

  • cpanminus
  • PEP 740
  • pylock.toml

Technologies named here

  • cpanminus
  • mitmproxy
  • Docker
  • WireGuard
  • Sigstore Rekor
  • Go checksum database
  • npm audit signatures
  • NuGet
  • pnpm audit signatures
  • gem cert --add
  • Ruby Gem and Bundler, Maven and Gradle
  • Bun and Yarn
  • pgpverify-maven-plugin with pgp-keys-map.list
  • Pip, uv, Poetry
  • Gradle dependency verification
  • dotnet nuget trust source and nuget.config
  • PowerShell
  • Gradle gradle.lockfile and Gem Gemfile.lock
  • Conda lock
  • Maven, R, PowerShellGet, PSResourceGet, cpanminus
  • pylock.toml
  • Cargo, CocoaPods, Composer, Conan, Conda, Pub, R, SwiftPM
  • Let's Encrypt
  • npm
  • pnpm
  • Go
  • Sigstore
  • Cargo
  • Composer
  • Trail of Bits
  • vcpkg
  • Conan
  • CocoaPods

Techniques named here

  • Active network attacker injecting code over unencrypted HTTP
  • Lockfile hash verification of downloaded packages
  • MD5 and SHA-1 collision risk in lockfile identifiers
  • Repository signing
  • Developer signing
  • Transparency logs
  • Proposed design

Also named here

  • Gamage et al.