Keegan Ryan of the security firm Trail of Bits set up a fake compromised package server and pointed 25 popular package managers at it. Two of them, Go and vcpkg, verified every package they downloaded, even without a lockfile. Without one, the other 23 installed whatever they were given.
Lockfiles didn't rescue most of them either.
The test
Ryan routed each tool's traffic through a proxy that could rewrite downloads, then installed quickstart projects with default settings, with and without a lockfile. An attack counted as a success if the injected code ran. The threat model assumes any web-facing server, such as a registry or a mirror, can be compromised, but backend secrets like private signing keys can't.
The lockfile is the safety net, and it's mostly unplugged
A lockfile pins every dependency's exact version and, ideally, a hash: a fingerprint the download has to match. Fewer than half of the 25 tools checked those hashes by default.
All 25 package managers, by default behavior. Source: Trail of Bits, October 14, 2026.
Nine verify when a lockfile is present, including npm, pnpm, Cargo, uv and Yarn. Five can verify, but only with extra steps. The last nine don't verify lockfile hashes at all, or the check doesn't apply to them.
Checks that don't stop anything
Tools with no check are the obvious failures. The sneakier ones run a check and install anyway:
-
Pub (Dart and Flutter) detects a hash mismatch, installs the package anyway and updates the lockfile to match, unless you pass
--enforce-lockfile. Some Flutter commands runpub gettoo, and can overwrite the lockfile unless called with--no-pub. - pnpm overwrote the lockfile on a mismatch until version 11.4, in May 2026, so the tampered package simply became the new truth.
- Composer (PHP) locks a Git commit, then downloads a ZIP archive it never checks against that commit.
- CocoaPods hashes the package spec but fetches the source by Git tag, which a compromised host could serve with different contents.
- cpanminus (Perl) still connects to its repository over plain HTTP by default.
Four outcomes when the download and the lockfile disagree. Only the green one protects you.
Why the defaults are like this
None of this is hidden; it's documented behavior. Lockfiles were built for reproducible installs, and as Ryan notes, reading them through a supply-chain lens "is a recent interpretation that can be at odds with their original design." Strict pinning also has a cost: every update, security fixes included, needs a lockfile change.
Go shows the alternative. It checks every hash against a signed checksum database that works like a transparency log, which makes targeted tampering much harder, at the cost of concentrating trust in a single private key. It was the only tool that verified every download and checked signatures by default. As Ryan puts it, "Package signing is in a similar state now to where HTTPS was a decade ago."
What to do this week
- Commit your lockfile, at least for applications. Norms for libraries vary by ecosystem.
-
Install in strict mode in CI:
npm ci,pnpm install --frozen-lockfile,uv sync --locked,pip install --require-hashes -r requirements.txt(with hashes generated bypip-compile --generate-hashes) ordart pub get --enforce-lockfile. -
If your tool needs extra steps, take them: the conda-lock library for Conda, a
verification-metadata.xmlfile for Gradle, theRestorePackagesWithLockFileproperty for NuGet. -
If your tool is in the last group, don't treat its lockfile as a security control. Maven can check signatures against trusted keys with the third-party
pgpverify-maven-plugin. Beyond that, the fallback Conan's developers suggest applies widely: if integrity is critical, run your own repository server.
Then test it: change one hash in your lockfile on purpose and confirm CI fails.
Caveats
Each tool was tested with one package, one version and one platform, Ubuntu, and some setups, like PowerShell and CocoaPods on Ubuntu, don't reflect typical use. The study shows that reasonable defaults can leave you exposed, not that every setup is.
Verification comes before detection
Most supply chain tooling hunts for packages already known to be bad. This study is about the step before that: making sure the package you got is the package you asked for. For most of the 25 tools, that check is off by default or missing. Where it exists, turning it on is about the cheapest security win you'll find.
If you check what your package manager does on a hash mismatch, I'd like to hear what you find.
Sources
- Package managers have an integrity problem, Keegan Ryan, Trail of Bits, October 14, 2026


Top comments (1)
One hop up from the package manager there is a dependency with no lockfile at all: the workflow's own steps. uses: org/action@v4 is a tag, and GitHub's API shows where a tag points now, not where it pointed last week. In March 2026 an attacker force pushed 76 of 77 version tags of trivy-action to credential stealing code, per Aqua's advisory.
The fix is pinning each action to a full commit SHA, with a catch from that same advisory: a trivy-action pinned to a commit from before April 2025 could still pull a malicious setup-trivy by tag. Pinning the top does not pin what it calls. Your "change one hash" test has a twin: grep your workflows for third party uses lines that don't end in a 40 character SHA.