Repository navigation
feat(artifact-cas): verify uploaded content against the declared digest - #3365
Conversation
AI Session Checks —
|
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
⚠️ iac-scan — 1 failing
| Status | Policy | Messages |
|---|---|---|
iac-misconfiguration |
|
PR info
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | pr-min-approvals |
- |
| ✅ Passed | pr-description-required |
- |
| ✅ Passed | pr-user-story-linked |
- |
⏭️ 2 scans not applied
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
View attestation ↗
View attestation ↗
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
All reported issues were addressed across 25 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
migmartri
left a comment
There was a problem hiding this comment.
In general it looks good to me, see my comments, some thoughts though
- I'd make this mode to be the only mode, not configurable, if it's going to be tmpDir that's ok
- What happened to the streaming capabilities you added in the other PR, why is it no longer needed? can we confirm that during the end to end input->stagingFS->blobFS we never load the whole file in memory?
d7ae4fa to
93c9dbf
Compare
VerificationUnit / integration (
End to end against a real S3 (MinIO, testcontainers, runs in CI)
End to end with the real CLI against a locally built control plane + CAS + MinIO
End to end on a kind cluster, chart-deployed, scratch image built from this branch
Interrupted uploads
|
@migmartri - the two points from your review, answered in the threads but summarised here: Make staging the only mode. Done. What happened to streaming. End to end memory. Bounded for the object stores, measured: +11.6 MB peak RSS for a |
There was a problem hiding this comment.
All reported issues were addressed across 27 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
93c9dbf to
901b2d8
Compare
afff50c to
557ef34
Compare
The Artifact CAS keys stored artifacts by their client-declared SHA256. This change adds end-to-end content-integrity verification to the upload path so the canonical key is only ever written from bytes CAS has confirmed hash to that digest, across all object-store backends (S3, S3 access point, Azure Blob), in addition to the OCI backend's existing layer-digest check. The CAS service streams each upload to a temporary file on a local staging volume, computes its SHA256 while receiving, and verifies it against the declared digest before handing the verified file to the backend. A mismatch is rejected with InvalidArgument and nothing is written to the backend. The upload path is consolidated into a single spill-then-upload flow shared by all backends, replacing the per-backend streaming branch and the StreamingUploader interface. The staging directory is configured through staging_dir and is required: the CAS refuses to start without one, and proves it is writable before serving traffic, so a misconfiguration surfaces at boot rather than as a healthy service that fails every upload. The Helm chart mounts a dedicated emptyDir for it. Each staging file is unlinked as soon as it is created and the upload proceeds through the open descriptor, so the kernel reclaims the space whenever the process exits, including when it is killed mid-upload, and the volume cannot accumulate partial artifacts. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: 5d0fc605-25b0-4bb3-900c-2c2a8fb4922f
557ef34 to
5384cdb
Compare
| type: "RuntimeDefault" | ||
|
|
||
|
|
||
| ## CAS upload staging. |
The Artifact CAS keyed stored artifacts by their client-declared SHA256 without ever
checking that the uploaded bytes hash to that digest. Only the OCI backend verified, via
its own layer-digest check. On S3, S3 access point and Azure Blob any identity holding
artifact:uploadcould store arbitrary content under an arbitrary digest key.The CAS now streams each upload to a local staging file, hashes it while receiving, and
hands the backend a file only once its digest matches the one the client declared. A
mismatch is rejected with
InvalidArgumentand nothing reaches the backend.What changed
streaming branch and the
StreamingUploaderinterface. Verification is no longersomething each backend implements, and any future backend inherits it.
descriptor, so the kernel reclaims the space whenever the process exits, including when
it is killed mid-upload. Nothing needs to sweep or reap the staging volume.
staging_diris required: the CAS refuses to start without one and proves it iswritable before serving traffic, so a misconfiguration fails at boot rather than
producing a healthy service that rejects every upload.
sizeLimit, since exceeding one evicts the Pod rather than failing the upload thatoverran it.
OCI backend still requires the whole layer in memory, unchanged here.
checksum precondition, which cannot be expressed for multipart uploads.
Addresses PFM-6988.
This contribution was produced with the assistance of Claude Code.