Repository navigation
Verify npm and Composer archives against their SHA-1 shasum - #479
Conversation
206506f to
a62f0d1
Compare
andrew
left a comment
There was a problem hiding this comment.
The npm side looks right: a usable dist.integrity still wins, v0.2.0 ParseSRI rejects the old sha1-<base64> values so those versions fall through to dist.shasum, and the ParseChecksum fallback in newIntegrityChecks lets readStored re-check a recorded sha1-<hex> on cache hits.
The Composer dev-branch case breaks cache hits. versions.integrity is one value per version PURL and GetCachedArtifact joins it onto every artifact of that version, but a dev version caches each commit's archive under its own filename. So: a lock file pinned to commit C0 fetches dev-main (unchecked, integrity NULL), another client fetches the listed head C1 (records C1's shasum on the shared row), and the next cache hit for C0 is verified against C1's shasum. It fails at EOF, the client gets an aborted download, and readStored discards the C0 archive. The refetch writes NULL again, which drops checking for C1's cache hits too. Every move of the branch head does the same to older pinned commits. TestComposerDownloadPinnedCommitIgnoresListedShasum only covers the first fetch, so it doesn't see this.
Please have composerIntegrity return "" for isDevVersion(version) so branch archives stay unverified. The listed shasum for a moving branch only describes one commit, and Composer checks dist.shasum client-side anyway. Add a test that fetches the pinned commit, then the head, then the pinned commit again from cache, and expects 200 with the original bytes.
v0.2.0 adds verification-only SHA-1 and MD5, algorithm-prefixed checksum parsing and exact-digest verification (git-pkgs/integrity#3).
npm versions published before dist.integrity existed carry only a SHA-1 dist.shasum, and Composer metadata lists only a SHA-1 dist.shasum, so neither could be checked. - Accept native integrity given as an algorithm-prefixed hex checksum, verified with integrity's exact-digest check, so a recorded "sha1-<hex>" is also checked on cache hits - npm: fall back to dist.shasum when dist.integrity is missing or cannot be parsed; a usable dist.integrity still takes precedence - Composer: verify an archive against its dist.shasum, except another commit of a branch fetched in place of the listed one, which the listed shasum does not describe - Fetch unverified as before when no shasum is listed, as Packagist does for GitHub archives, or it is not a SHA-1 digest
A version's integrity is a single value shared by every archive cached for it, but a dev branch caches one archive per commit. Recording the listed head's shasum made a cache hit for an older pinned commit fail its check, so its archive was discarded and fetched again, which cleared the head's recorded shasum in turn. The listed shasum only ever describes the current head, and Composer checks dist.shasum itself. - Skip the shasum for dev versions - Test a pinned commit, then the head, then both again from the cache
a62f0d1 to
ba5e6e8
Compare
|
Confirmed, and fixed in ba5e6e8 as you suggested.
The branch is also rebased onto main now that #473 is in, so it carries only the v0.2.0 bump and the shasum commits. |
andrew
left a comment
There was a problem hiding this comment.
The dev-branch fix and the cache-hit regression test cover it, thanks.
One small leftover: with dev versions returning early, the listed parameter of composerIntegrity is dead, since only branch versions can resolve to an unlisted URL in upstreamDistURL. The parameter, the downloadURL == listedURL argument and the "A shasum also only describes the listed archive" sentence can go.
Builds on #473 (merged) and git-pkgs/integrity v0.2.0, which includes git-pkgs/integrity#3 and adds the SHA-1 support this needs.
This is the follow-up andrew asked for in #473. It adds the SHA-1 checks, now that the integrity module can verify registry checksums.
Changes
newIntegrityChecksaccepts native integrity given as a single algorithm-prefixed hex checksum (sha1-<hex>) when it isn't SRI. That checksum is verified with integrity's exact-digest check (Result.VerifyDigest). It goes through the same stored, streamed and cache-hit paths as SRI, so a recorded shasum is re-checked on cache hits too.dist.shasumwhendist.integrityis missing or can't be parsed, which is the case for versions published before integrity existed. A usabledist.integritystill takes precedence.dist.shasum, which most private repositories (Satis, Private Packagist, GitLab) list. Dev branches (dev-*) aren't checked. Their listed shasum describes only the current head, but every commit of the branch is cached under the same version, and a version records a single integrity value. A cache hit for an older pinned commit would be checked against the head's shasum. Composer checksdist.shasumclient-side anyway.Tests
npm_integrity_test.go, stored and streamed:dist.shasumreturns 502 (or aborts the stream) and nothing is cached;dist.integritywins overdist.shasum;composer_shasum_test.go:integrity_test.go: ansha1-<hex>native checksum is verified and a mismatch is reported.go test -race ./...andgolangci-lintv2.13.1 pass.