Expand description
CLEANLIB-870 (integrity half) — artifact-bytes digest verification.
Status: the verification PRIMITIVE below is ready; there is nothing to
wire it to yet. Confirmed live (2026-09-16) that GET /v1/fetch/...
carries no digest-bearing response header at all — no ETag, no
Content-MD5, no Digest, no x-goog-hash (the catalog is GCS-backed;
a proxied GCS response would normally carry the latter, but the App does
not pass it through). The signed attestation’s artifact_hash field is a
hash of the serialized verdict, not of the artifact, and changes on
every request — it structurally cannot serve as a content digest. So
there is currently no real per-artifact digest anywhere on the wire for
crate::transport::Client::fetch_artifact_stream to check the streamed
bytes against. That is a server-side gap (filed separately per the
ticket’s own “RELATED” section), not something this crate can close on
its own by inventing a digest source.
What IS shippable now, independent of that blocker: this module’s
verify_sha256_digest — a pure, tested comparison function — plus the
CLI’s fetch command already writing to a same-directory temp file and
only renaming it onto the real destination on full success (so any
failure, a future digest mismatch included, leaves no partial output at
the real path — see cleanlib-cli/src/commands/fetch.rs). The day a real
per-artifact digest becomes available (a response header, most likely,
matching how X-CleanLibrary-Decision/X-CleanLibrary-Reason are
already read in transport.rs::emit_decision_headers for the sibling
verdict-gating half of the same ticket), wiring it in is: read the
digest, call verify_sha256_digest against the fully-streamed bytes
before the CLI’s rename-into-place step, and fail closed (delete the temp
file, non-zero exit) on DigestMismatch.
Structs§
- Digest
Mismatch - The streamed bytes’ SHA-256 did not match the expected digest. Carries both hex-encoded digests so a caller can report exactly what diverged (never just “integrity check failed” with no evidence).
Functions§
- verify_
sha256_ digest - Verify
bytesagainst anexpectedSHA-256 digest, hex-encoded (lowercase or uppercase, with or without a leadingsha256:prefix — both are real-world shapes a server might emit and rejecting on formatting alone would be its own false-negative bug). ReturnsOk(())on a match,DigestMismatchon any divergence — deliberately fail CLOSED: a malformedexpectedvalue (wrong length, non-hex characters) is NOT treated as “nothing to check” and passed through, it is compared byte-for-byte and will not match a real digest, which correctly surfaces as a mismatch rather than silently skipping verification.