feat: store policy evaluations in CAS during attestation push#2949
Conversation
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Add a new policyEvaluationsRef field to the attestation predicate that will hold a CAS reference to the externalized policy evaluations bundle. This includes a setter method on RendererV02 and a forwarding method on AttestationRenderer to allow the push action to set the reference after uploading the bundle to CAS. Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Serialize all PolicyEvaluation protos into a PolicyEvaluationBundle, upload to CAS, and set the resulting ResourceDescriptor on the renderer so it appears in the attestation predicate. The upload is best-effort: if CAS is unavailable, a warning is logged and the inline policyEvaluations field remains for backward compatibility. Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
…rage Convert separate ref tests to table-driven test. Add debug log when CAS backend is unavailable during push. Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
| // uploadPolicyEvaluationsBundle serializes policy evaluations as a protobuf bundle, | ||
| // uploads to CAS, and returns a ResourceDescriptor referencing the uploaded object. | ||
| // Returns (nil, nil) when there are no evaluations or no uploader. | ||
| func uploadPolicyEvaluationsBundle(ctx context.Context, evaluations []*v1.PolicyEvaluation, uploader casclient.Uploader) (*intoto.ResourceDescriptor, error) { |
There was a problem hiding this comment.
Do we have any code already for uploading to CAS that we can reuse, maybe from the crafter material part? I want to make sure that we don't try reimplement the same logic. Today we upload AI_CONFIG materials, etc
There was a problem hiding this comment.
The existing uploadAndCraft() in pkg/attestation/crafter/materials/materials.go is material-specific — it takes a file path, produces an Attestation_Material proto, and handles inline fallback, size checks, skip-upload flags, and material annotations. None of that applies here.
The actual shared primitive is Uploader.Upload(ctx, reader, filename, digest) from the casclient package, which is what we use directly. The uploadPolicyEvaluationsBundle function adds only the protobuf serialization + SHA256 + ResourceDescriptor construction, all specific to this use case.
Key difference from materials: when no CAS backend is available, materials fall back to inline storage (InlineCas = true). Here we intentionally skip (the existing inline policyEvaluations predicate field already serves as the fallback, per the spec).
Check evaluations count before establishing CAS connection to avoid unnecessary network round-trip. Consolidate redundant conditional blocks into a single if/else. Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/cli/pkg/action/attestation_push.go">
<violation number="1" location="app/cli/pkg/action/attestation_push.go:233">
P2: The debug message is too specific for the condition and can misreport backend errors as "inline" mode, making operational debugging harder.</violation>
<violation number="2" location="app/cli/pkg/action/attestation_push.go:237">
P1: CAS upload failure is now treated as fatal, which breaks the intended best-effort behavior for policy-evaluations CAS storage during attestation push.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } else { | ||
| ref, uploadErr := uploadPolicyEvaluationsBundle(ctx, evaluations, casBackend.Uploader) | ||
| if uploadErr != nil { | ||
| return nil, fmt.Errorf("uploading policy evaluations bundle to CAS: %w", uploadErr) |
There was a problem hiding this comment.
P1: CAS upload failure is now treated as fatal, which breaks the intended best-effort behavior for policy-evaluations CAS storage during attestation push.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/cli/pkg/action/attestation_push.go, line 237:
<comment>CAS upload failure is now treated as fatal, which breaks the intended best-effort behavior for policy-evaluations CAS storage during attestation push.</comment>
<file context>
@@ -230,12 +230,13 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru
if uploadErr != nil {
- action.Logger.Warn().Err(uploadErr).Msg("failed to upload policy evaluations bundle to CAS")
- } else if ref != nil {
+ return nil, fmt.Errorf("uploading policy evaluations bundle to CAS: %w", uploadErr)
+ }
+ if ref != nil {
</file context>
| return nil, fmt.Errorf("uploading policy evaluations bundle to CAS: %w", uploadErr) | |
| action.Logger.Warn().Err(uploadErr).Msg("failed to upload policy evaluations bundle to CAS") |
| } | ||
|
|
||
| if getCASErr != nil || casBackend.Uploader == nil { | ||
| action.Logger.Debug().Msg("CAS backend is inline, skipping policy evaluations bundle upload") |
There was a problem hiding this comment.
P2: The debug message is too specific for the condition and can misreport backend errors as "inline" mode, making operational debugging harder.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/cli/pkg/action/attestation_push.go, line 233:
<comment>The debug message is too specific for the condition and can misreport backend errors as "inline" mode, making operational debugging harder.</comment>
<file context>
@@ -230,12 +230,13 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru
if getCASErr != nil || casBackend.Uploader == nil {
- action.Logger.Debug().Msg("CAS backend not available, skipping policy evaluations bundle upload")
+ action.Logger.Debug().Msg("CAS backend is inline, skipping policy evaluations bundle upload")
} else {
ref, uploadErr := uploadPolicyEvaluationsBundle(ctx, evaluations, casBackend.Uploader)
</file context>
| action.Logger.Debug().Msg("CAS backend is inline, skipping policy evaluations bundle upload") | |
| action.Logger.Debug().Err(getCASErr).Msg("CAS backend not available, skipping policy evaluations bundle upload") |
Summary
PolicyEvaluationBundleprotobuf message that wraps a flat list ofPolicyEvaluationfor CAS storagepolicyEvaluationsRefResourceDescriptor in the attestation predicate alongside the existing inlinepolicyEvaluationsfieldCloses #2947