Repository navigation
Verify npm tarballs against dist.integrity - #473
Conversation
andrew
left a comment
There was a problem hiding this comment.
Verifying npm and Composer archives against their metadata is the right thing to do, but checksum.go builds a second verification path next to the one the proxy already has.
internal/handler/integrity.go wraps github.com/git-pkgs/integrity, and newIntegrityChecks(contentHash, native) already verifies sha256/sha384/sha512 SRI on both the stored path and the streamed path (wrapFailOnMismatch). checkCache also re-verifies versions.integrity on cache hits through the same function. Recording the declared checksum there and reusing integrityChecks removes the need for checksumHasher, checksumVerifyingReader, upstreamHashForChecksum and the isDeclaredChecksum exception in artifactHashMatches, and it closes the limitation in the description, since cached artifacts would be re-checked against the metadata checksum too.
The one thing the integrity module can't do yet is SHA-1. That belongs in the module rather than in the proxy: git-pkgs/integrity#1 covers verification-only SHA-1 and MD5, hex checksum parsing, an exact-digest verify path and an explicit strength ranking (the current enum-order comparison in strongestAlgorithm becomes unsafe once weaker algorithms exist).
So the change that can land now is npm dist.integrity through the existing integrityChecks. The dist.shasum fallback for npm and the Composer dist.shasum check need integrity#1 and a dependency bump first, and should come in a separate PR after that.
Reusing integrity.Result.Verify also fixes a bug in npmVersionChecksum: it takes the first digest of the strongest algorithm, while SRI allows several digests of that algorithm and any of them may match, so a valid tarball listing two sha512 entries would currently fail as a mismatch.
The proxy cached the first tarball upstream sent for a version without checking it against the packument, so a tampered tarball was served to every client after it. - Pass the packument's dist.integrity to the existing integrity checks, so a fresh tarball is verified as it is stored or streamed; a mismatch is discarded and returns 502, or aborts the streamed response - Record it with the cached version, so cache hits are verified against it through the same check as before - Verify with integrity.Result.Verify, which accepts any digest of the strongest algorithm listed - Fetch unverified as before when dist.integrity is missing or cannot be parsed, or the packument cannot be fetched
b222883 to
2d258dd
Compare
|
Thanks, agreed on all points. I've narrowed this PR to npm
For the SHA-1 part:
|
|
@simonchrz i just merged the integrity pr and tagged v0.2.0: https://github.com/git-pkgs/integrity/releases/tag/v0.2.0 |
|
@andrew thanks for the quick merge and release!
#479 sits on top of #473, so #473 should go in first. After that I'll rebase #479 onto main so it carries only the v0.2.0 bump and the shasum commit. |
npm tarballs were cached without being checked against the
dist.integrityin their packument. Whatever bytes the first fetch got were stored and then served to every client.Changes
dist.integritygoes to the proxy's integrity checks (newIntegrityChecks/integrity.Result.Verify). No second verification path is added.proxy_integrity_failures_total, and the client gets a 502.stream_artifacts): the check runs at end of stream throughwrapFailOnMismatch, and the response is aborted on a mismatch.versions.integrity, soreadStoredre-checks cache hits against it with the check that already exists.dist.integrityis missing or can't be parsed, or the packument can't be fetched, the tarball is fetched unverified as before.GetOrFetchArtifactFromURLWithIntegrityis the entry point. The declared integrity is part of the fetch-coalescing key.Not in this PR
npm's
dist.shasumfallback and Composer'sdist.shasumcheck need SHA-1. Those are in git-pkgs/integrity#3, which implements integrity#1, and draft #479, which builds on it.Tests
npm_integrity_test.go, on both the stored and the streamed path:dist.shasum, is served unverified.I also broke the stored check and the streamed check in turn, and the tests failed both times.
go test -race ./...andgolangci-lintv2.13.1 pass.