If a lockfile entry's integrity hash and source URL are both edited, why does install-time verification still pass?
answer
- what is the hash compared against
- self-referential check
- channel tamper evidence, not authorship
- edit both fields in one commit
- regenerate in a clean environment and diff
basics
~20 sThe hash is compared with the bytes fetched from the source named in that same entry. An attacker who edits both simply records a hash matching their own payload. Lockfile hashes detect tampering in transit, not a tampered lockfile.
solid answer
~50 sInstall-time hash checking is self-referential: it verifies that the bytes actually downloaded match the hash written next to them in the lockfile. That covers the path from wherever the artifact was published, through mirrors, proxies and local caches, to your disk - real tamper detection for a real threat. It says nothing about whether the lockfile line itself is honest. Anyone who can edit the entry can point it at a different tarball and write the matching hash in the same commit, and every install afterwards verifies cleanly and reproducibly. So the lockfile is the statement of trust, and the hash only enforces it faithfully. What actually catches this is treating the lock as reviewed code, regenerating it from the manifest in a clean environment and comparing, restricting the resolved source to approved hosts, and flagging any entry whose hash changed while its version did not.
code
json · 7 lines"node_modules/acme-date-utils": {
"version": "4.2.1",
"resolved": "https://cdn-mirror.example.net/acme-date-utils/-/acme-date-utils-4.2.1.tgz",
"integrity": "sha512-<digest of whatever sits at that URL>",
"hasInstallScript": true
},
...go deeper
Know that the hash in a lockfile is compared with the bytes that were downloaded, and that whoever can edit the entry can edit the hash in the same change.
Explain the trust boundary the hash covers - published artifact through mirrors and caches to your disk - and why it makes no claim about who authored the lock entry.
Describe controls that do catch it: regenerating the lock in a clean environment and comparing, restricting resolved sources to approved hosts, and alerting on hash-only changes.
Argue where real assurance comes from - records kept outside the repository, publisher signatures, and limiting who may author lock changes at all - rather than from stronger hashing.
## What the check actually compares When an install reads a lock entry, it fetches the bytes from the source recorded in that entry and hashes them. If the digest equals the one recorded in the same entry, the install proceeds. The comparison is between *downloaded bytes* and *the lockfile's own claim*. Nothing external is consulted. That makes the guarantee precise and narrow: | Question | Does a lockfile hash answer it? | |---|---| | Are these the bytes the lockfile asked for? | Yes | | Did they survive the mirror, proxy and cache untouched? | Yes | | Is the lockfile line itself trustworthy? | No | | Did the genuine maintainer publish these bytes? | No | | Is the code safe to run? | No | The first two are worth having. A cache-poisoning proxy, a compromised mirror or a corrupted CDN object is caught immediately, and reproducibility across machines becomes provable. But an integrity hash is a statement *about the lockfile*, so it cannot be evidence *for* the lockfile. ## The edit that defeats it Someone with commit access - a legitimate contributor, a compromised contributor account, or a bot whose token leaked - changes one entry in a refresh of several hundred lines. The version string stays exactly as it was. The resolved source moves to a host they control, or to an artifact they were able to place on an accepted host. The hash is recomputed over their payload. The commit is well formed, the tests pass, the install is reproducible on every machine, and every subsequent verification succeeds - because verification is doing exactly what it was designed to do. Note what did *not* help. Commit signing proves who authored the change, which is valuable against impersonation, but a real contributor's signed commit carries a hostile lock line perfectly well. A stronger hash algorithm changes nothing, because the hash was never the weak link. A scanner comparing declared versions against advisories sees a version it already knew about. ## Controls that do catch it **Regenerate and compare.** Resolve the manifest in a clean, controlled environment and diff the result against the committed lockfile. Any entry the regeneration does not reproduce is either a hand edit or evidence that resolution is not deterministic - both worth a human. This is the single highest-value control because it converts a reading problem into a machine check. **Constrain the resolved source.** If every entry is supposed to come from one approved host, an entry pointing elsewhere is a policy violation you can detect mechanically, without any judgement about the package. **Classify diffs instead of reading them.** Hash changed while version did not; source host changed; a package appeared that was not in the graph. These three categories are rare in honest refreshes and are precisely where a hostile edit lands. **Get an out-of-repo record.** Some ecosystems back their pins with a public, append-only checksum log recorded outside your repository, so the first time anyone anywhere fetches a given module version its digest is fixed globally and a divergent copy is detectable rather than merely unfamiliar. Where the ecosystem offers that, it converts trust-on-first-use into a cross-checkable claim - and it is why proposals to exempt a module from the checksum log deserve a real decision, not a shrug. **Verify the publisher, not just the bytes.** A signature over the artifact by a known key answers "who vouches for this", which the hash never claimed to answer. Byte integrity, publisher identity and code safety are three separate properties, and conflating them is the most common wrong answer in this domain. ## The framing to carry into an interview A lockfile hash is *tamper evidence for a channel*, not *provenance for a decision*. It hardens the path between the package's source and your disk. The decision about which package, which version and which bytes are acceptable is made by whoever wrote the line - so the security of the lockfile ultimately rests on who may write to it and who reads what they wrote.
- What would a public, append-only checksum log add that a lockfile hash cannot?It puts the record outside the repository the attacker is editing. The digest for a given package version is fixed globally the first time it is seen and is append-only, so a divergent copy is contradicted by a record the editor does not control. A lockfile hash is only ever a claim by whoever wrote the lockfile.
- Would requiring signed commits on the pull request have caught this edit?No. Commit signing establishes who authored a change, which stops impersonation and helps attribution after the fact. It makes no statement about content, so a legitimate contributor - or a stolen but properly configured signing setup - delivers the hostile lock line with a perfectly valid signature.
- Your regeneration check keeps producing diffs on honest refreshes. What does that tell you?That resolution is not deterministic in your setup - typically because the regeneration runs against a different resolver version, a different platform, or a source whose contents move. Fix the reproducer until honest refreshes are byte-identical; until then the check produces noise, reviewers learn to ignore it, and the signal you built it for is gone.
A wax seal proves nobody opened the letter on the way to you. It proves nothing about whether the person who sealed it wrote the truth inside.
saying these in an interview costs you the question
- Says the hash is checked against the registry's own published record
- Claims a matching hash proves the package is not malicious
- Thinks a stronger hash algorithm would prevent this
- Believes signed commits would have detected the swapped payload
- Assumes the install fails because the version number did not change