diff --git a/app/cli/cmd/attestation_init.go b/app/cli/cmd/attestation_init.go index 3648812a0..f2ccd94af 100644 --- a/app/cli/cmd/attestation_init.go +++ b/app/cli/cmd/attestation_init.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2025 The Chainloop Authors. +// Copyright 2024-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -127,7 +127,7 @@ func newAttestationInitCmd() *cobra.Command { return newGracefulError(err) } - res, err := statusAction.Run(cmd.Context(), attestationID) + res, err := statusAction.Run(cmd.Context(), attestationID, action.WithSkipPolicyEvaluation()) if err != nil { return newGracefulError(err) } diff --git a/app/cli/pkg/action/attestation_push.go b/app/cli/pkg/action/attestation_push.go index 046dd7efc..5e952cf00 100644 --- a/app/cli/pkg/action/attestation_push.go +++ b/app/cli/pkg/action/attestation_push.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2025 The Chainloop Authors. +// Copyright 2024-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -100,7 +100,7 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru if err != nil { return nil, fmt.Errorf("creating status action: %w", err) } - attestationStatus, err := statusAction.Run(ctx, attestationID) + attestationStatus, err := statusAction.Run(ctx, attestationID, WithSkipPolicyEvaluation()) if err != nil { return nil, fmt.Errorf("creating running status action: %w", err) } @@ -200,6 +200,9 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru return nil, fmt.Errorf("evaluating attestation policies: %w", err) } + // Update the status result with the evaluated policies + attestationStatus.PolicyEvaluations, attestationStatus.HasPolicyViolations = getPolicyEvaluations(crafter) + // render final attestation with all the evaluated policies inside envelope, bundle, err := renderer.Render(ctx) if err != nil { diff --git a/app/cli/pkg/action/attestation_status.go b/app/cli/pkg/action/attestation_status.go index 70bccb691..72573d560 100644 --- a/app/cli/pkg/action/attestation_status.go +++ b/app/cli/pkg/action/attestation_status.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2025 The Chainloop Authors. +// Copyright 2024-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -93,6 +93,9 @@ func NewAttestationStatus(cfg *AttestationStatusOpts) (*AttestationStatus, error }, nil } +// WithSkipPolicyEvaluation skips re-evaluating policies against the attestation statement. +// Existing policy evaluations already stored in the crafting state (e.g. from material additions) +// will still be included in the result. func WithSkipPolicyEvaluation() func(*AttestationStatus) { return func(opts *AttestationStatus) { opts.skipPolicyEvaluation = true @@ -159,10 +162,12 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string, if err := c.EvaluateAttestationPolicies(ctx, attestationID, statement); err != nil { return nil, fmt.Errorf("evaluating attestation policies: %w", err) } - - res.PolicyEvaluations, res.HasPolicyViolations = getPolicyEvaluations(c) } + // Always read existing policy evaluations from crafting state, + // even when skipping re-evaluation (e.g. material-level evaluations from add) + res.PolicyEvaluations, res.HasPolicyViolations = getPolicyEvaluations(c) + if v := workflowMeta.GetVersion(); v != nil { res.WorkflowMeta.ProjectVersion = &ProjectVersion{ Version: v.GetVersion(), @@ -210,7 +215,8 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string, return res, nil } -// getPolicyEvaluations retrieves both material-level and attestation-level policy evaluations and returns if it has violations +// getPolicyEvaluations retrieves both material-level and attestation-level policy evaluations from the crafting state +// and returns whether any violations exist. func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool) { // grouped by material name evaluations := make(map[string][]*PolicyEvaluation)