From 7d43ed2071eee4dd7cb958d24952761f6957c7db Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Thu, 13 Aug 2026 11:55:34 +0200 Subject: [PATCH] fix(attestation): omit inline policy evaluations when CAS ref is set When an external CAS backend is enabled, the attestation push uploads the PolicyEvaluationBundle to CAS and adds a policyEvaluationsRef to the predicate, but it still embedded the full inline policyEvaluations as well. The payload carried the evaluations twice and could exceed the control-plane gRPC receive limit at push time. Drop the inline policyEvaluations from the predicate once the ref is emitted. The no-CAS / inline-backend path keeps the inline copy, and consumers keep the fallback for attestations that predate the ref. Predicate summary counters are derived from the attestation directly, so they remain accurate. Signed-off-by: Javier Rodriguez --- pkg/attestation/renderer/chainloop/v02.go | 6 ++++++ pkg/attestation/renderer/chainloop/v02_test.go | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/attestation/renderer/chainloop/v02.go b/pkg/attestation/renderer/chainloop/v02.go index f45171de4..d1a6e8571 100644 --- a/pkg/attestation/renderer/chainloop/v02.go +++ b/pkg/attestation/renderer/chainloop/v02.go @@ -291,6 +291,12 @@ func (r *RendererV02) predicate() (*structpb.Struct, error) { SigningTSA: r.att.GetSigningOptions().GetTimestampAuthorityUrl(), } + // When evaluations are offloaded to CAS (ref set), keep only the ref and drop + // the inline copy; the summary counters above are derived independently. + if r.policyEvaluationsRef != nil { + p.PolicyEvaluations = nil + } + // transform to structpb.Struct in a two steps process // 1 - ProvenancePredicate -> json // 2 - json -> structpb.Struct diff --git a/pkg/attestation/renderer/chainloop/v02_test.go b/pkg/attestation/renderer/chainloop/v02_test.go index 72b8755a2..97690fde9 100644 --- a/pkg/attestation/renderer/chainloop/v02_test.go +++ b/pkg/attestation/renderer/chainloop/v02_test.go @@ -480,6 +480,8 @@ func TestPredicatePolicyEvaluationsRef(t *testing.T) { if !tc.wantRef { assert.Nil(t, predicate.PolicyEvaluationsRef) + // Without a ref (no-CAS backend) the evaluations stay inline. + assert.NotEmpty(t, predicate.PolicyEvaluations) return } @@ -487,6 +489,10 @@ func TestPredicatePolicyEvaluationsRef(t *testing.T) { assert.Equal(t, tc.ref.Name, predicate.PolicyEvaluationsRef.Name) assert.Equal(t, tc.ref.MediaType, predicate.PolicyEvaluationsRef.MediaType) assert.Equal(t, tc.ref.Digest["sha256"], predicate.PolicyEvaluationsRef.Digest["sha256"]) + + // With a ref present (CAS offload) the predicate must not also carry + // the inline evaluations. + assert.Empty(t, predicate.PolicyEvaluations) }) } }