Your lockfile is a supply chain attack surface you're ignoring
Von Seaworthy · 5. Juni 2026
Photo by John Salvino on Unsplash
Why attackers stopped touching your source code
Most code reviews follow a predictable pattern. Someone opens a pull request, reviewers read through the changed .ts or .py files, leave some comments about naming, and merge. The lockfile diff, if it appears at all, gets a quick glance and a mental note that "dependencies changed." Nobody reads it. Attackers have noticed.
Over the last two years, there has been a clear shift in supply chain attacks toward lockfile manipulation. The technique is straightforward: instead of compromising a package's source repository or publishing a typosquatted package, an attacker submits a pull request to a target project with a "helpful" change that also modifies package-lock.json or pnpm-lock.yaml to point a dependency at a malicious version, a compromised registry mirror, or an entirely different package.
The source code looks clean. The review passes. The next npm ci on CI pulls the attacker's payload.
What a tampered lockfile actually looks like
In a legitimate project, a lockfile entry for a small utility might look like this:
"node_modules/some-util": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/some-util/-/some-util-1.2.3.tgz",
"integrity": "sha512-abc123realHashHere..."
}
After tampering, a reviewer who glances rather than reads might see:
"node_modules/some-util": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/some-util/-/some-util-1.2.3.tgz",
"integrity": "sha512-xyz789differentHash..."
}
Same version. Same resolved URL. Different integrity hash. That is enough to pull a different tarball entirely if the attacker controls a registry mirror or has managed to replace the tarball at the source. The version number being identical is the entire point; it does not trip any version-check tooling.
There is also a simpler variant: the resolved field itself points to a different URL. This works when the target project does not enforce registry allow-listing in their npm or pnpm configuration. An attacker can point resolved at a package hosted on a registry they control, with a name that matches a legitimate one. npm will download from the URL in the lockfile, not from the configured registry.
The pnpm variant deserves a separate mention because pnpm lockfiles use a YAML format that is significantly harder to scan visually. A changed checksum: field buried in hundreds of lines of dependency tree output is genuinely easy to miss.
The CI trap
Here is the part that makes this worse. Most projects configure CI to run npm ci rather than npm install. This is good practice: npm ci installs exactly what is in the lockfile, ignoring package.json version ranges. But it also means CI will faithfully install a tampered dependency without complaint. Nothing in the CI output indicates that anything went wrong. The malicious package runs in your pipeline with access to your secrets and build artifacts.
I have seen teams with otherwise careful security posture skip lockfile review on the grounds that "it's generated." It is generated. That does not make it trustworthy.
Locking it down
Make lockfile diffs a required review item. This does not mean reading every line, it means checking for specific things:
- Changes to
resolvedURLs that point somewhere other than the official registry integrityhash changes that do not correspond to a version bump- Packages appearing that are not in package.json as a direct dependency
Run npm audit or pnpm audit in CI with --audit-level=high and fail the build on violations. This does not catch all tampered packages, but it catches known malicious ones that have been reported to the registry.
For pnpm projects, the following CI pattern detects drift between package.json and the committed lockfile:
# Install exactly what the lockfile specifies
pnpm install --frozen-lockfile
# Then verify the lockfile matches what a fresh install would produce
pnpm install --lockfile-only
git diff --exit-code pnpm-lock.yaml
If that second command exits non-zero, the lockfile in the repository does not match what a clean install would generate. That is worth investigating before you merge.
The review culture problem
The real issue is not tooling. Tooling helps, but the underlying problem is that lockfiles have been mentally categorized as generated noise for so long that even security-conscious developers skip the diff. A tampered lockfile in a PR from an outside contributor is exactly the kind of thing a code review is supposed to catch.
One concrete change: add a line to your PR review checklist that says "lockfile changes reviewed." Not "lockfile changes noted." Reviewed. If a contributor's change modified package-lock.json in ways that are not explained by the package.json diff, ask why before merging.
Warning: if you are accepting external contributions to a public repository, this is not a theoretical risk. Projects with thousands of stars are being targeted precisely because attackers know that busy maintainers skip lockfile diffs.
This is not a novel attack class. It has persisted because no one made lockfile review a priority, and because the tooling that exists to catch it is not wired into most review workflows by default.
Seaworthy flags unexpected lockfile integrity changes as part of its dependency hygiene checks.
Dieser Artikel wurde von KI generiert und fasst öffentlich verfügbare Quellen zusammen.
Seaworthy scannt dein Repository nach den Problemen, die in Artikeln wie diesem behandelt werden.
Sicherheitslücken, offengelegte Geheimnisse und Fehlkonfigurationen — erkannt, bevor du bereitstellst. Kostenlos, kein Konto nötig.