You open a pull request. It touches package-lock.json. GitHub shows you 4,000 lines of churned resolved URLs and integrity hashes. You scroll, your eyes glaze, you click Approve.
That habit is exactly how the bad stuff gets in. Nearly every npm supply-chain incident this year entered the same way: a new — often transitive — package landed in someone's lockfile, and nobody looked. Because the lockfile diff is unreadable by design.
Before merging, I want the answer to one question: what actually changed in my dependency tree? Not four thousand lines of hashes — just what's new, what's gone, what jumped a major version.
So I built locksift. Zero dependencies, no account, no network.
What it does
$ npx locksift package-lock.json --git
Added (2) — new in tree, review these
+ ansi-styles@6.2.1
+ picocolors@1.0.1
Removed (1)
- request@2.88.2
Changed (2)
↑ chalk 4.1.2 → 5.3.0 [major]
↓ semver 7.5.4 → 7.3.8 [downgrade]
+2 -1 ~2 (2 major, 1 downgrade)
--git diffs your working-tree lockfile against HEAD (or any ref you name) — the PR-review case. You can also just hand it two lockfiles.
The Added section is the one that matters for a supply-chain review: those are the packages that just entered your tree, including transitive ones you never explicitly asked for. Downgrades and major bumps get flagged too, since those are the changes most worth a second look.
Why not just git diff?
Because git diff works on text. A one-line npm install can rewrite thousands of lockfile lines — reordered entries, new hashes, changed resolved URLs — none of which tell you the thing you care about. locksift parses the lockfile structurally and diffs the actual dependency set. Signal, not churn.
And to be clear about what it is not: locksift is not a scanner. It won't tell you a package is malicious. It tells you a package is new — so you can decide whether to look. That's the step missing from most workflows today. Not more scanning; just making the diff reviewable enough that a human will actually read it.
Drop it into CI
# Fail the job if the lockfile changed at all
locksift package-lock.json --git --exit-code
# Or pipe just the newly added packages into your own tooling
locksift package-lock.json --git --added-only --json
Install
npx locksift ... # Node build (npm)
pip install locksift # Python build — same tool
There are two builds — Node and Python — that produce byte-for-byte identical output, so it slots into whatever toolchain your repo already speaks.
Supported lockfiles
package-lock.json (lockfileVersion 1, 2, and 3) and pipenv's Pipfile.lock. Both are plain JSON — which is exactly what lets locksift stay dependency-free.
Design notes, for the curious
- Zero dependencies, both builds. Node uses only its stdlib; Python only its stdlib. A diff tool that dragged in 40 packages of its own would be a bad joke.
- Pure core. All parsing/diffing is pure functions (text in, data out); file and git I/O live in the CLI. That keeps it trivially testable — and is how the two builds stay in lockstep.
-
A viewer by default. It exits
0even when there are changes; add--exit-codeto turn it into a gate. (Thegit diff --exit-codeconvention.)
Links
- npm: https://www.npmjs.com/package/locksift
- PyPI: https://pypi.org/project/locksift/
- Source: https://github.com/jjdoor/locksift
When was the last time you actually read a lockfile diff before approving a PR? What would make you trust a dependency change — and what do you use for this today? Curious whether anyone gates CI on lockfile changes already.
Top comments (0)