From 38b3c4a874492315dbbbc709ec6a7839a40b186a Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 21:44:28 +0100 Subject: [PATCH 1/6] fix(cli): skip policy evaluation during attestation init status display The init command calls attestation status immediately after initialization to show the current state. This was triggering policy evaluation against an empty attestation statement, which is unnecessary and confusing. Fixes #2761 Signed-off-by: Miguel Martinez --- app/cli/cmd/attestation_init.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) } From c996a47325c991e26b35b74484d0dc846fbe05d7 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 21:47:55 +0100 Subject: [PATCH 2/6] fix(cli): skip redundant policy evaluation in status calls from init and push The init command calls attestation status after initialization to display state. This triggered policy evaluation against an empty attestation, which is unnecessary. The push command also called status for display purposes and then evaluated policies again separately with the final statement, causing duplicate evaluations. Pass WithSkipPolicyEvaluation() to both internal status calls since the mechanism already existed but wasn't being used. Fixes #2761 Signed-off-by: Miguel Martinez --- app/cli/pkg/action/attestation_push.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/cli/pkg/action/attestation_push.go b/app/cli/pkg/action/attestation_push.go index 046dd7efc..59b23e5e8 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) } From baf5122bbb580906d3f613a83d883aaa58ce269a Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 21:57:38 +0100 Subject: [PATCH 3/6] fix(cli): show policy evaluations in push output The status call in push was skipping policy evaluation (to avoid duplicate work), but this also skipped reading existing evaluations from the crafting state. Move GetPolicyEvaluations outside the skip block so status always reads existing evaluations, and populate the status result in push after the actual policy evaluation completes. Fixes #2761 Signed-off-by: Miguel Martinez --- app/cli/pkg/action/attestation_add.go | 4 ++-- app/cli/pkg/action/attestation_push.go | 3 +++ app/cli/pkg/action/attestation_status.go | 13 ++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/cli/pkg/action/attestation_add.go b/app/cli/pkg/action/attestation_add.go index 082db2322..42346d884 100644 --- a/app/cli/pkg/action/attestation_add.go +++ b/app/cli/pkg/action/attestation_add.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. @@ -178,7 +178,7 @@ func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestat return nil, err } - policyEvaluations, _ := getPolicyEvaluations(crafter) + policyEvaluations, _ := GetPolicyEvaluations(crafter) return policyEvaluations, nil } diff --git a/app/cli/pkg/action/attestation_push.go b/app/cli/pkg/action/attestation_push.go index 59b23e5e8..1d44f5a40 100644 --- a/app/cli/pkg/action/attestation_push.go +++ b/app/cli/pkg/action/attestation_push.go @@ -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..59579dee1 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. @@ -159,10 +159,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,8 +212,9 @@ 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 -func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool) { +// 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) var hasViolations bool From c2e14b5dbb98b5afc0b471fab12dee86f070a8bc Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 21:59:37 +0100 Subject: [PATCH 4/6] fix(cli): keep getPolicyEvaluations unexported All callers are within the action package, no need to export it. Signed-off-by: Miguel Martinez --- app/cli/pkg/action/attestation_add.go | 2 +- app/cli/pkg/action/attestation_push.go | 2 +- app/cli/pkg/action/attestation_status.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/cli/pkg/action/attestation_add.go b/app/cli/pkg/action/attestation_add.go index 42346d884..c6101bb26 100644 --- a/app/cli/pkg/action/attestation_add.go +++ b/app/cli/pkg/action/attestation_add.go @@ -178,7 +178,7 @@ func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestat return nil, err } - policyEvaluations, _ := GetPolicyEvaluations(crafter) + policyEvaluations, _ := getPolicyEvaluations(crafter) return policyEvaluations, nil } diff --git a/app/cli/pkg/action/attestation_push.go b/app/cli/pkg/action/attestation_push.go index 1d44f5a40..5e952cf00 100644 --- a/app/cli/pkg/action/attestation_push.go +++ b/app/cli/pkg/action/attestation_push.go @@ -201,7 +201,7 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru } // Update the status result with the evaluated policies - attestationStatus.PolicyEvaluations, attestationStatus.HasPolicyViolations = GetPolicyEvaluations(crafter) + attestationStatus.PolicyEvaluations, attestationStatus.HasPolicyViolations = getPolicyEvaluations(crafter) // render final attestation with all the evaluated policies inside envelope, bundle, err := renderer.Render(ctx) diff --git a/app/cli/pkg/action/attestation_status.go b/app/cli/pkg/action/attestation_status.go index 59579dee1..b66052715 100644 --- a/app/cli/pkg/action/attestation_status.go +++ b/app/cli/pkg/action/attestation_status.go @@ -163,7 +163,7 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string, // 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) + res.PolicyEvaluations, res.HasPolicyViolations = getPolicyEvaluations(c) if v := workflowMeta.GetVersion(); v != nil { res.WorkflowMeta.ProjectVersion = &ProjectVersion{ @@ -212,9 +212,9 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string, return res, nil } -// GetPolicyEvaluations retrieves both material-level and attestation-level policy evaluations from the crafting state +// 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) { +func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool) { // grouped by material name evaluations := make(map[string][]*PolicyEvaluation) var hasViolations bool From ef10fa19ca0818126d819ca3f3d207154069afd4 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 22:02:09 +0100 Subject: [PATCH 5/6] fix(cli): revert unnecessary change to attestation_add.go Signed-off-by: Miguel Martinez --- app/cli/pkg/action/attestation_add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cli/pkg/action/attestation_add.go b/app/cli/pkg/action/attestation_add.go index c6101bb26..082db2322 100644 --- a/app/cli/pkg/action/attestation_add.go +++ b/app/cli/pkg/action/attestation_add.go @@ -1,5 +1,5 @@ // -// Copyright 2024-2026 The Chainloop Authors. +// Copyright 2024-2025 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. From a5c4d4dd9e87bafcdfe4bd6fe89525d386ae4271 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Date: Thu, 19 Feb 2026 22:06:55 +0100 Subject: [PATCH 6/6] fix(cli): document WithSkipPolicyEvaluation behavior Signed-off-by: Miguel Martinez --- app/cli/pkg/action/attestation_status.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/cli/pkg/action/attestation_status.go b/app/cli/pkg/action/attestation_status.go index b66052715..72573d560 100644 --- a/app/cli/pkg/action/attestation_status.go +++ b/app/cli/pkg/action/attestation_status.go @@ -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