Context
src/review/lockfile-tamper.ts's scanPackageLockPatch (lines ~115-224) scans a package-lock.json unified diff for tamper risk: a resolved/integrity change WITHOUT the same lockfile entry's own "version" field genuinely changing. When an entry's own "node_modules/<pkg>": { header line isn't visible anywhere in the diff (git's default 3-line context doesn't guarantee it survives — the documented #7778 case), the scanner buckets the change under a synthetic "${path}#unattributed-N" key (activeUnknownKey, declared line 133-134).
The bug: activeUnknownKey is reset to null on any depth-0 closing brace (lines 179-187), not just at the boundary of the specific entry it belongs to. When the SAME lockfile entry's version bump and its resolved/integrity change happen to land in two separate hunks of the same patch (a realistic occurrence — git only gives ~3 lines of context, and an intervening } between the two changed fields is enough to reset tracking), each hunk gets a brand-new unattributed bucket with its own unknownEntrySeq. Bucket #1 records the version change; bucket #2 records the resolved/integrity change — but they never merge, so bucket #2's resolvedOrIntegrityChanged && !versionChanged check (line 246) is wrongly true even though the same entry legitimately bumped both fields together. This fires a false lockfile_tamper_risk finding on a routine npm install/npm update.
The 527-line existing test suite (test/unit/lockfile-tamper.test.ts) thoroughly covers the single-hunk unattributed case (#7778) but has no test for this cross-hunk scenario.
Requirements
Deliverables
Test Coverage Requirements
Standard 99%+ Codecov patch coverage, branch-counted, on every changed line/branch in lockfile-tamper.ts per this repo's codecov.yml (src/** is covered; only_pulls: true, threshold: 0%). Add both a positive case (still flags a real cross-hunk tamper) and a negative case (no longer false-flags a legitimate cross-hunk version+integrity bump) — this is a correctness fix, so both the old wrong behavior and the new correct behavior must be explicitly asserted, not just covered for line count.
Expected Outcome
A legitimate npm lockfile update whose version bump and resolved/integrity change land in different diff hunks (realistic any time an entry has enough intervening unchanged fields to exceed git's 3-line context) no longer produces a false lockfile_tamper_risk finding, while a genuine tamper (integrity changed without a real version bump) is still caught even when split across hunks.
Links & Resources
Context
src/review/lockfile-tamper.ts'sscanPackageLockPatch(lines ~115-224) scans apackage-lock.jsonunified diff for tamper risk: aresolved/integritychange WITHOUT the same lockfile entry's own"version"field genuinely changing. When an entry's own"node_modules/<pkg>": {header line isn't visible anywhere in the diff (git's default 3-line context doesn't guarantee it survives — the documented #7778 case), the scanner buckets the change under a synthetic"${path}#unattributed-N"key (activeUnknownKey, declared line 133-134).The bug:
activeUnknownKeyis reset tonullon any depth-0 closing brace (lines 179-187), not just at the boundary of the specific entry it belongs to. When the SAME lockfile entry's version bump and its resolved/integrity change happen to land in two separate hunks of the same patch (a realistic occurrence — git only gives ~3 lines of context, and an intervening}between the two changed fields is enough to reset tracking), each hunk gets a brand-new unattributed bucket with its ownunknownEntrySeq. Bucket #1 records the version change; bucket #2 records the resolved/integrity change — but they never merge, so bucket #2'sresolvedOrIntegrityChanged && !versionChangedcheck (line 246) is wronglytrueeven though the same entry legitimately bumped both fields together. This fires a falselockfile_tamper_riskfinding on a routinenpm install/npm update.The 527-line existing test suite (
test/unit/lockfile-tamper.test.ts) thoroughly covers the single-hunk unattributed case (#7778) but has no test for this cross-hunk scenario.Requirements
scanPackageLockPatchso that unattributed version/resolved/integrity signals for the same package are correlated across hunk boundaries within one file's patch, instead of being permanently isolated the moment a depth-0}is seen between them. A reasonable approach: when a header is unrecoverable (the existing Lockfile tamper detector can be silently evaded by placing the changed field outside the entry header's diff-context window #7778 case), key the fallback bucket by the package name parsed from surrounding context lines when one is recoverable, falling back to the existing per-block sequence numbering only when no package name can be determined at all.test/unit/lockfile-tamper.test.ts(the single-hunk Lockfile tamper detector can be silently evaded by placing the changed field outside the entry header's diff-context window #7778 case, off-registry URL detection, the lockfileVersion-1 bare-key path) must keep passing unchanged.Deliverables
scanPackageLockPatchinsrc/review/lockfile-tamper.tsthat correlates unattributed version/resolved/integrity signals for the same package name across hunk boundaries within one file's patch.test/unit/lockfile-tamper.test.tsreproducing the cross-hunk scenario: a"version"add/remove pair in one hunk, and a"resolved"/"integrity"add/remove pair for the SAME package in a separate hunk (separated by an intervening depth-0}), both with no visible entry header — asserting NOlockfile_tamper_riskfinding fires.Test Coverage Requirements
Standard 99%+ Codecov patch coverage, branch-counted, on every changed line/branch in
lockfile-tamper.tsper this repo'scodecov.yml(src/**is covered;only_pulls: true,threshold: 0%). Add both a positive case (still flags a real cross-hunk tamper) and a negative case (no longer false-flags a legitimate cross-hunk version+integrity bump) — this is a correctness fix, so both the old wrong behavior and the new correct behavior must be explicitly asserted, not just covered for line count.Expected Outcome
A legitimate npm lockfile update whose version bump and resolved/integrity change land in different diff hunks (realistic any time an entry has enough intervening unchanged fields to exceed git's 3-line context) no longer produces a false
lockfile_tamper_riskfinding, while a genuine tamper (integrity changed without a real version bump) is still caught even when split across hunks.Links & Resources
src/review/lockfile-tamper.ts(scanPackageLockPatch, lines ~115-224)test/unit/lockfile-tamper.test.ts