feat(threatcrush-scan): verify the tarball hash before installing it - #959
Merged
Conversation
Haven's maintainer declined the workflow and asked for "exact version + integrity hash". The version pin shipped; the hash did not. This is the half that was missing, and their framing was the correct one — the two are not the same guarantee. A version pin says which release to fetch. It does not say the bytes are the ones that release was published with, and the party answering "which version" is the party serving the bytes. Pinning alone narrows the window to a re-publish of the same version; it does not close it. So the install is now download, hash, compare, install: npm pack --pack-destination "$RUNNER_TEMP" fetch, do not install openssl dgst -sha512 | openssl base64 hash it locally compare against threatcrushIntegrity refuse on mismatch npm install -g --ignore-scripts "$tarball" install the bytes we hashed Verified npm pack reproduces the published tarball byte for byte: its sha512 equals dist.integrity for 0.11.0 exactly, so the comparison is against the registry's own published digest and not a repack of it. Two details worth keeping: Into RUNNER_TEMP, never the checkout. `npm pack` writes to the working directory by default, and a stray .tgz in the tree is something this workflow then scans and reports findings on. The mismatch is not retried, where the download is. A blip and a mismatch are different events: one is the network, the other is the registry handing back bytes nobody signed off on, and retrying that just asks again until it succeeds. threatcrushIntegrity defaults to the 0.11.0 hash and must be bumped with the spec. A hash from a different version fails closed, which is the right direction to fail but a confusing one to debug, so both the manifest and the README say so. Empty skips verification, for a consumer pointing the spec at something they build themselves. Verified all three paths against the block as written: correct hash installs, a wrong hash exits 1 without installing and prints expected and received, and an empty hash warns and proceeds. Pack to 1.3.0 — a new input. Signed-off-by: Anthony Ettinger <anthony@chovy.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
ThreatCrush Security Scan311 finding(s) HIGH/CRITICAL: 24 | MEDIUM: 51 | LOW: 236
…and 261 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
This was referenced Aug 14, 2026
feat(tcfeed): say that the workflow verifies the hash, not just the pin
profullstack/threatcrush#135
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Haven#532's maintainer declined the workflow and asked for "exact version + integrity hash". The version pin shipped; the hash did not. This is the missing half — and their framing was the correct one, because the two are not the same guarantee.
A version pin says which release to fetch. It does not say the bytes are the ones that release was published with, and the party answering "which version" is the party serving the bytes. Pinning alone narrows the window to a re-publish of the same version; it does not close it.
What the install does now
Verified
npm packreproduces the published tarball byte for byte — its sha512 equalsdist.integrityfor 0.11.0 exactly — so the comparison is against the registry's own published digest, not a repack of it.Two details worth keeping
Into
RUNNER_TEMP, never the checkout.npm packwrites to the working directory by default, and a stray.tgzin the tree is something this workflow then scans and reports findings on.The mismatch is not retried, where the download is. A blip and a mismatch are different events: one is the network, the other is the registry handing back bytes nobody signed off on — and retrying that just asks again until it succeeds.
The new input
threatcrushIntegritydefaults to the 0.11.0 hash and must be bumped with the spec. A hash from a different version fails closed, which is the right direction to fail but a confusing one to debug, so both the manifest and README say so. Read it fromnpm view <spec> dist.integrity. Empty skips verification, for a consumer pointing the spec at something they build themselves.Verification
All three paths, against the block as written:
Pack to 1.3.0 — a new input.
🤖 Generated with Claude Code