
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.
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-lockfileis passed.flutter runandflutter testcallflutter pub get, so they can overwritepubspec.lockunless called with--no-pub. - pnpm overwrote the lockfile after a mismatch until version 11.4 (May 2026). Before that,
--frozen-lockfilewas 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.lockfileand Gem'sGemfile.lockhave no hashes. Gradle'sverification-metadata.xmldoes have content hashes. - Maven, R, PowerShellGet, PSResourceGet and cpanminus have no dedicated lockfile.
- pip (experimental
pylock.toml), Conda (optional Conda lock) and NuGet (needsRestorePackagesWithLockFile) 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:
| Manager | Status |
|---|---|
| npm | npm install skips signature checks; npm audit signatures checks them |
| pnpm | pnpm audit signatures fetches the registry keys from the possibly malicious registry, so an attacker can swap in their own key |
| Bun, Yarn | no equivalent command |
| NuGet | default signatureValidationMode is accept; set it to require and configure trusted signers, e.g. dotnet nuget trust source |
| Gem, Bundler | add developer keys manually with gem cert --add |
| Maven | third-party pgpverify-maven-plugin with pgp-keys-map.list |
| Gradle | dependency verification |
| pip, uv, Poetry | cannot verify PEP 740 attestations |
| PowerShell | no Authenticode verification on non-Windows |
| Cargo, CocoaPods, Composer, Conan, Conda, Pub, R, SwiftPM | no 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), NuGetrequiremode 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, Gradletrusted-keysandpgp-keys-map.listalready 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.
Related in Supply Chain


