chore(deps): reject an unreadable lockfile instead of failing open
From Copilot's eleventh pass of #1962, and the most consequential kind of bug for a gate: it failed *open*. `topLevelLockVersions` returns an empty map for anything without a v2+ `packages` table — a lockfileVersion 1 file, or a malformed one. `main()` fed that straight into the comparison, so such an install contributed no holders at all, and a genuine skew among the remaining installs was reported as aligned. The guard would have said OK to exactly what it exists to catch. `hasReadableLockShape` now gates every discovered lockfile before any comparison, requiring a `packages` object carrying the `""` root entry npm always writes — "has a packages key" is not enough. An unreadable one is named and exits 1, telling you to regenerate it. A JSON parse failure likewise reports the file rather than dying on a bare SyntaxError with no path in it. The empty-map behavior of the pure helper is kept and its test now says why it is safe: the shape check rejects those inputs before they can reach `findSkew`. Three regression cases: a v1 lockfile in a fixture that IS skewed (so failing open would show as a pass), a `packages` table with no root entry, and the unit table for the predicate. Fixtures now write the `""` root entry, matching real npm output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
This commit is contained in:
parent
32c827f089
commit
af63a07817
1 changed files with 137 additions and 14 deletions
|
|
@ -8,6 +8,7 @@ import { test } from "node:test";
|
|||
import assert from "node:assert/strict";
|
||||
import {
|
||||
findSkew,
|
||||
hasReadableLockShape,
|
||||
importedPackageNames,
|
||||
isSharedSourceFile,
|
||||
majorOf,
|
||||
|
|
@ -243,10 +244,38 @@ test("topLevelLockVersions: nested duplicates are ignored", () => {
|
|||
});
|
||||
|
||||
test("topLevelLockVersions: a malformed or empty lockfile yields nothing", () => {
|
||||
// Safe as a pure helper *because* `hasReadableLockShape` rejects these before
|
||||
// any comparison — an empty map reaching `findSkew` is the fail-open path.
|
||||
for (const lock of [undefined, null, {}, { packages: {} }])
|
||||
assert.equal(topLevelLockVersions(lock).size, 0);
|
||||
});
|
||||
|
||||
test("hasReadableLockShape: only a v2+ packages table with a root entry (Copilot, #1962)", () => {
|
||||
assert.equal(
|
||||
hasReadableLockShape({ lockfileVersion: 3, packages: { "": {} } }),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
hasReadableLockShape({
|
||||
lockfileVersion: 2,
|
||||
packages: { "": {}, "node_modules/zod": { version: "4.4.3" } },
|
||||
}),
|
||||
true,
|
||||
);
|
||||
const rejected = [
|
||||
undefined,
|
||||
null,
|
||||
{},
|
||||
{ packages: null },
|
||||
{ packages: [] }, // an array has no `""` key
|
||||
{ packages: {} }, // no root entry
|
||||
{ packages: { "node_modules/zod": { version: "4.4.3" } } },
|
||||
{ lockfileVersion: 1, dependencies: { zod: { version: "4.4.3" } } },
|
||||
];
|
||||
for (const lock of rejected)
|
||||
assert.equal(hasReadableLockShape(lock), false, JSON.stringify(lock));
|
||||
});
|
||||
|
||||
test("findSkew: reports a package held at two versions", () => {
|
||||
const installs = [
|
||||
{ dir: ".", versions: new Map([["zod", "4.3.6"]]) },
|
||||
|
|
|
|||
Loading…
Reference in a new issue