Comparing two lockfiles
scan answers is this tree bad. A reviewer looking at a pull request has a
narrower question, and it is not the same one: did this change make it worse.
$ ./target/release/stranger diff fixtures/npm-l.package-lock.json fixtures/poisoned.package-lock.json
fixtures/npm-l.package-lock.json -> fixtures/poisoned.package-lock.json
added 3
chalck@5.3.0
expres@4.18.2
lodahs@4.17.21
introduced 4 findings
chalck@5.3.0 not in corpus · d=1 from "chalk" · root-only, no parent
expres@4.18.2 not in corpus · d=1 from "express" · root-only, no parent
lodahs@4.17.21 not in corpus · d=1 from "lodash" · root-only, no parent
lodahs@4.17.21 runs code at install time · lockfile records the flag, not the script
poisoned.package-lock.json is npm-l with three names planted in it. A scan
of it also reports 55 version-drift findings and 35 trivial packages, every one
of which was already in npm-l. The diff is the three that arrived.
Why the gate is not just two scans
--fail-on means something different here, deliberately. On scan it is the
worst finding in the tree; on diff it is the worst finding the change
introduced.
$ ./target/release/stranger diff fixtures/npm-l.package-lock.json fixtures/poisoned.package-lock.json --fail-on high -q
$ echo $?
1
Reverse the arguments and the same two files pass, because taking a problem out is not putting one in:
$ ./target/release/stranger diff fixtures/poisoned.package-lock.json fixtures/npm-l.package-lock.json --fail-on high -q
$ echo $?
0
While scan fails on that tree in both directions:
$ ./target/release/stranger scan fixtures/poisoned.package-lock.json --fail-on high -q
$ echo $?
1
That asymmetry is the reason the subcommand exists. A repository with 211
trivial packages fails a scan gate on every pull request until somebody turns
the gate off; it passes a diff gate until a pull request adds something.
Findings are matched by rule and package, not by version
A bumped dependency keeps its findings. esbuild@0.20.0 with an install script
becoming esbuild@0.21.0 with an install script is not one finding fixed and
one introduced — it is a version change, which the changed block prints, and
no change in risk.
The cost is real: a package already flagged for a rule can change version and the new version's finding is not called new.
A finding can move without a package moving
The two lockfiles do not have to be the same format — only the same ecosystem —
and the formats do not all record the same things. npm records install scripts;
yarn v1 has no field for them. So migrating a project
between the two changes the finding set while added, removed and changed
are all empty, and that is a real result rather than a glitch: the packages did
not move, but what is now visible about them did.
diff prints those findings and gates on them. The alternative — deciding
there is nothing to say because no package moved — is how it printed
no change to the dependency tree and exited 1 at the same time, which in CI
is a red build with nothing on screen to explain it.
In CI
$ git show HEAD~1:package-lock.json > /tmp/before.package-lock.json
$ stranger diff /tmp/before.package-lock.json package-lock.json --fail-on high
Exit 1 means this change introduced something at or above the threshold. See exit codes, and gate a pull request for the whole-tree version of the same job.
JSON
One object, not a stream — a diff is one comparison however many packages it touched. No timing field, for the same reason the scan object has none: two runs over the same pair have to produce the same bytes.
$ ./target/release/stranger diff --format json fixtures/npm-l.package-lock.json fixtures/poisoned.package-lock.json | head -c 120
{"old":"fixtures/npm-l.package-lock.json","new":"fixtures/poisoned.package-lock.json","added":["chalck@5.3.0","expres@4.
What it refuses
Two different ecosystems. A Cargo.lock against a package-lock.json parses
fine and produces a diff in which every package was added and every package was
removed — a confident, detailed, meaningless answer.
$ ./target/release/stranger diff fixtures/cargo-s.Cargo.lock fixtures/npm-xs.package-lock.json
stranger: fixtures/cargo-s.Cargo.lock and fixtures/npm-xs.package-lock.json are different ecosystems (crates.io and npm); there is nothing to compare
Two formats of one ecosystem are fine, and that diff is the one somebody migrating from npm to pnpm wants.