diff --git a/app/cli/cmd/attestation_push.go b/app/cli/cmd/attestation_push.go index c3872911e..2492f019a 100644 --- a/app/cli/cmd/attestation_push.go +++ b/app/cli/cmd/attestation_push.go @@ -22,6 +22,7 @@ import ( schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" "github.com/spf13/cobra" + "github.com/spf13/viper" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -71,9 +72,15 @@ func newAttestationPushCmd() *cobra.Command { return fmt.Errorf("getting executable information: %w", err) } a, err := action.NewAttestationPush(&action.AttestationPushOpts{ - ActionsOpts: ActionOpts, KeyPath: pkPath, BundlePath: bundle, - CLIVersion: info.Version, CLIDigest: info.Digest, - LocalStatePath: attestationLocalStatePath, + ActionsOpts: ActionOpts, + KeyPath: pkPath, + BundlePath: bundle, + CLIVersion: info.Version, + CLIDigest: info.Digest, + CASURI: viper.GetString(confOptions.CASAPI.viperKey), + CASCAPath: viper.GetString(confOptions.CASCA.viperKey), + ConnectionInsecure: apiInsecure(), + LocalStatePath: attestationLocalStatePath, SignServerOpts: &action.SignServerOpts{ CAPath: signServerCAPath, AuthClientCertPath: signServerAuthCertPath, diff --git a/app/cli/pkg/action/attestation_push.go b/app/cli/pkg/action/attestation_push.go index 9a013b5b5..cb6845596 100644 --- a/app/cli/pkg/action/attestation_push.go +++ b/app/cli/pkg/action/attestation_push.go @@ -16,7 +16,9 @@ package action import ( + "bytes" "context" + "crypto/sha256" "encoding/json" "fmt" "os" @@ -25,9 +27,13 @@ import ( pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" "github.com/chainloop-dev/chainloop/pkg/attestation" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter" + v1 "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1" "github.com/chainloop-dev/chainloop/pkg/attestation/renderer" + crChainloop "github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop" "github.com/chainloop-dev/chainloop/pkg/attestation/signer" + "github.com/chainloop-dev/chainloop/pkg/casclient" "github.com/chainloop-dev/chainloop/pkg/policies" + intoto "github.com/in-toto/attestation/go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" "google.golang.org/grpc" @@ -38,9 +44,11 @@ import ( type AttestationPushOpts struct { *ActionsOpts KeyPath, CLIVersion, CLIDigest, BundlePath string - - LocalStatePath string - SignServerOpts *SignServerOpts + CASURI string + CASCAPath string + ConnectionInsecure bool + LocalStatePath string + SignServerOpts *SignServerOpts } // SignServerOpts holds SignServer integration options @@ -60,6 +68,9 @@ type AttestationResult struct { type AttestationPush struct { *ActionsOpts keyPath, cliVersion, cliDigest, bundlePath string + casURI string + casCAPath string + connectionInsecure bool localStatePath string signServerOpts *SignServerOpts *newCrafterOpts @@ -68,14 +79,17 @@ type AttestationPush struct { func NewAttestationPush(cfg *AttestationPushOpts) (*AttestationPush, error) { opts := []crafter.NewOpt{crafter.WithLogger(&cfg.Logger), crafter.WithAuthRawToken(cfg.AuthTokenRaw)} return &AttestationPush{ - ActionsOpts: cfg.ActionsOpts, - keyPath: cfg.KeyPath, - cliVersion: cfg.CLIVersion, - cliDigest: cfg.CLIDigest, - bundlePath: cfg.BundlePath, - signServerOpts: cfg.SignServerOpts, - localStatePath: cfg.LocalStatePath, - newCrafterOpts: &newCrafterOpts{cpConnection: cfg.CPConnection, opts: opts}, + ActionsOpts: cfg.ActionsOpts, + keyPath: cfg.KeyPath, + cliVersion: cfg.CLIVersion, + cliDigest: cfg.CLIDigest, + bundlePath: cfg.BundlePath, + casURI: cfg.CASURI, + casCAPath: cfg.CASCAPath, + connectionInsecure: cfg.ConnectionInsecure, + signServerOpts: cfg.SignServerOpts, + localStatePath: cfg.LocalStatePath, + newCrafterOpts: &newCrafterOpts{cpConnection: cfg.CPConnection, opts: opts}, }, nil } @@ -205,6 +219,29 @@ func (action *AttestationPush) Run(ctx context.Context, attestationID string, ru // Update the status result with the definitive push-phase evaluation against the final statement attestationStatus.PolicyEvaluations, attestationStatus.HasPolicyViolations = getPolicyEvaluations(crafter) + // Upload policy evaluations bundle to CAS when an external backend is available + if evaluations := crafter.CraftingState.GetAttestation().GetPolicyEvaluations(); !crafter.CraftingState.DryRun && len(evaluations) > 0 { + casBackend := &casclient.CASBackend{Name: "not-set"} + workflowRunID := crafter.CraftingState.GetAttestation().GetWorkflow().GetWorkflowRunId() + _, connectionCloserFn, getCASErr := getCASBackend(ctx, attClient, workflowRunID, action.casCAPath, action.casURI, action.connectionInsecure, action.Logger, casBackend) + if connectionCloserFn != nil { + // nolint: errcheck + defer connectionCloserFn() + } + + if getCASErr != nil || casBackend.Uploader == nil { + action.Logger.Debug().Msg("CAS backend is inline, skipping policy evaluations bundle upload") + } else { + ref, uploadErr := uploadPolicyEvaluationsBundle(ctx, evaluations, casBackend.Uploader) + if uploadErr != nil { + return nil, fmt.Errorf("uploading policy evaluations bundle to CAS: %w", uploadErr) + } + if ref != nil { + renderer.SetPolicyEvaluationsRef(ref) + } + } + } + // render final attestation with all the evaluated policies inside envelope, bundle, err := renderer.Render(ctx) if err != nil { @@ -318,3 +355,31 @@ func decodeEnvelope(rawEnvelope []byte) (*dsse.Envelope, error) { return envelope, nil } + +// 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) { + if len(evaluations) == 0 || uploader == nil { + return nil, nil + } + + bundle := &v1.PolicyEvaluationBundle{Evaluations: evaluations} + data, err := protojson.Marshal(bundle) + if err != nil { + return nil, fmt.Errorf("marshaling policy evaluation bundle: %w", err) + } + + hexDigest := fmt.Sprintf("%x", sha256.Sum256(data)) + digest := fmt.Sprintf("sha256:%s", hexDigest) + + if _, err := uploader.Upload(ctx, bytes.NewReader(data), "policy-evaluations.json", digest); err != nil { + return nil, fmt.Errorf("uploading policy evaluation bundle: %w", err) + } + + return &intoto.ResourceDescriptor{ + Name: "policy-evaluations", + Digest: map[string]string{"sha256": hexDigest}, + MediaType: crChainloop.PolicyEvaluationsBundleMediaType, + }, nil +} diff --git a/app/cli/pkg/action/attestation_push_test.go b/app/cli/pkg/action/attestation_push_test.go new file mode 100644 index 000000000..49e7dc713 --- /dev/null +++ b/app/cli/pkg/action/attestation_push_test.go @@ -0,0 +1,122 @@ +// +// Copyright 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package action + +import ( + "context" + "crypto/sha256" + "fmt" + "testing" + + v1 "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1" + "github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop" + "github.com/chainloop-dev/chainloop/pkg/casclient" + casclientmock "github.com/chainloop-dev/chainloop/pkg/casclient/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" +) + +func TestUploadPolicyEvaluationsBundle(t *testing.T) { + testCases := []struct { + name string + evaluations []*v1.PolicyEvaluation + uploader func(t *testing.T) casclient.Uploader + wantRef bool + wantErr bool + }{ + { + name: "nil evaluations returns nil ref", + evaluations: nil, + wantRef: false, + }, + { + name: "empty evaluations returns nil ref", + evaluations: []*v1.PolicyEvaluation{}, + wantRef: false, + }, + { + name: "nil uploader returns nil ref", + evaluations: []*v1.PolicyEvaluation{ + {Name: "test-policy"}, + }, + wantRef: false, + }, + { + name: "successful upload returns ref with correct digest and media type", + evaluations: []*v1.PolicyEvaluation{ + {Name: "test-policy", MaterialName: "sbom"}, + }, + uploader: func(t *testing.T) casclient.Uploader { + t.Helper() + m := casclientmock.NewUploader(t) + m.On("Upload", mock.Anything, mock.Anything, "policy-evaluations.json", mock.MatchedBy(func(digest string) bool { + return len(digest) > 7 && digest[:7] == "sha256:" + })).Return(&casclient.UpDownStatus{Filename: "policy-evaluations.json"}, nil) + return m + }, + wantRef: true, + }, + { + name: "upload failure returns error", + evaluations: []*v1.PolicyEvaluation{ + {Name: "test-policy"}, + }, + uploader: func(t *testing.T) casclient.Uploader { + t.Helper() + m := casclientmock.NewUploader(t) + m.On("Upload", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("upload failed")) + return m + }, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var uploader casclient.Uploader + if tc.uploader != nil { + uploader = tc.uploader(t) + } + + ref, err := uploadPolicyEvaluationsBundle(context.Background(), tc.evaluations, uploader) + if tc.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + + if !tc.wantRef { + assert.Nil(t, ref) + return + } + + require.NotNil(t, ref) + assert.Equal(t, "policy-evaluations", ref.Name) + assert.Equal(t, chainloop.PolicyEvaluationsBundleMediaType, ref.MediaType) + assert.NotEmpty(t, ref.Digest["sha256"]) + + // Verify the digest matches what we'd expect from serializing the bundle + bundle := &v1.PolicyEvaluationBundle{Evaluations: tc.evaluations} + data, err := protojson.Marshal(bundle) + require.NoError(t, err) + expectedDigest := fmt.Sprintf("%x", sha256.Sum256(data)) + assert.Equal(t, expectedDigest, ref.Digest["sha256"]) + }) + } +} diff --git a/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts b/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts index 6f5514a9d..74b8d396e 100644 --- a/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts +++ b/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts @@ -327,6 +327,11 @@ export interface PolicyEvaluation_RawResult { output: Uint8Array; } +/** Bundle of all policy evaluations for an attestation, stored as a CAS object. */ +export interface PolicyEvaluationBundle { + evaluations: PolicyEvaluation[]; +} + export interface Commit { hash: string; /** Commit authors might not include email i.e "Flux <>" */ @@ -3042,6 +3047,70 @@ export const PolicyEvaluation_RawResult = { }, }; +function createBasePolicyEvaluationBundle(): PolicyEvaluationBundle { + return { evaluations: [] }; +} + +export const PolicyEvaluationBundle = { + encode(message: PolicyEvaluationBundle, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.evaluations) { + PolicyEvaluation.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PolicyEvaluationBundle { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePolicyEvaluationBundle(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.evaluations.push(PolicyEvaluation.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): PolicyEvaluationBundle { + return { + evaluations: Array.isArray(object?.evaluations) + ? object.evaluations.map((e: any) => PolicyEvaluation.fromJSON(e)) + : [], + }; + }, + + toJSON(message: PolicyEvaluationBundle): unknown { + const obj: any = {}; + if (message.evaluations) { + obj.evaluations = message.evaluations.map((e) => e ? PolicyEvaluation.toJSON(e) : undefined); + } else { + obj.evaluations = []; + } + return obj; + }, + + create, I>>(base?: I): PolicyEvaluationBundle { + return PolicyEvaluationBundle.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): PolicyEvaluationBundle { + const message = createBasePolicyEvaluationBundle(); + message.evaluations = object.evaluations?.map((e) => PolicyEvaluation.fromPartial(e)) || []; + return message; + }, +}; + function createBaseCommit(): Commit { return { hash: "", diff --git a/app/controlplane/pkg/biz/casmapping.go b/app/controlplane/pkg/biz/casmapping.go index a51d118b1..a4b9d340d 100644 --- a/app/controlplane/pkg/biz/casmapping.go +++ b/app/controlplane/pkg/biz/casmapping.go @@ -240,5 +240,15 @@ func (uc *CASMappingUseCase) LookupDigestsInAttestation(att *dsse.Envelope, dige } } + // Include the policy evaluations bundle if stored in CAS + if ref := predicate.GetPolicyEvaluationsRef(); ref != nil { + if d, ok := ref.Digest["sha256"]; ok { + references = append(references, &CASMappingLookupRef{ + Name: ref.Name, + Digest: fmt.Sprintf("sha256:%s", d), + }) + } + } + return references, nil } diff --git a/app/controlplane/pkg/biz/casmapping_test.go b/app/controlplane/pkg/biz/casmapping_test.go index ea3cf11b2..5bb757128 100644 --- a/app/controlplane/pkg/biz/casmapping_test.go +++ b/app/controlplane/pkg/biz/casmapping_test.go @@ -1,5 +1,5 @@ // -// Copyright 2023-2025 The Chainloop Authors. +// Copyright 2023-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. @@ -126,6 +126,28 @@ func (s *casMappingSuite) TestLookupDigestsInAttestation() { }, }, }, + { + name: "with-policy-evaluations-ref", + attPath: "testdata/attestations/full-with-policy-evaluations-ref.json", + want: []*biz.CASMappingLookupRef{ + { + Name: "attestation", + Digest: "sha256:724b081c081fe79e308d100ccdeba8ecc77052a9e76ad638fdfe8a8db96b1c9b", + }, + { + Name: "skynet-sbom", + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + }, + { + Name: "skynet2-sbom", + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + }, + { + Name: "policy-evaluations", + Digest: "sha256:aabbccdd00112233aabbccdd00112233aabbccdd00112233aabbccdd00112233", + }, + }, + }, { name: "invalid-file", attPath: "testdata/attestations/invalid.json", diff --git a/app/controlplane/pkg/biz/testdata/attestations/full-with-policy-evaluations-ref.json b/app/controlplane/pkg/biz/testdata/attestations/full-with-policy-evaluations-ref.json new file mode 100644 index 000000000..7a8902ef5 --- /dev/null +++ b/app/controlplane/pkg/biz/testdata/attestations/full-with-policy-evaluations-ref.json @@ -0,0 +1,10 @@ +{ + "payloadType": "application/vnd.in-toto+json", + "payload": "eyJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLCAicHJlZGljYXRlVHlwZSI6ICJjaGFpbmxvb3AuZGV2L2F0dGVzdGF0aW9uL3YwLjIiLCAic3ViamVjdCI6IFt7Im5hbWUiOiAiY2hhaW5sb29wLmRldi93b3JrZmxvdy9vbmx5LXNib20iLCAiZGlnZXN0IjogeyJzaGEyNTYiOiAiMzAzNmYyZTVkNzA5YTIzODA4ZWFhMDU1NzBmMTcxOGFmZmY5MmQ0YWQzNWUyMzJjMTM4NzM4MzcyYWM5M2RmYiJ9fV0sICJwcmVkaWNhdGUiOiB7Im1ldGFkYXRhIjogeyJuYW1lIjogIm9ubHktc2JvbSIsICJwcm9qZWN0IjogImZvbyIsICJ0ZWFtIjogIiIsICJpbml0aWFsaXplZEF0IjogIjIwMjMtMDYtMjNUMTM6MDA6MjkuMTgzNjE4OTY2WiIsICJmaW5pc2hlZEF0IjogIjIwMjMtMDYtMjNUMTU6MDA6NDMuNzA5OTcwNjcrMDI6MDAiLCAid29ya2Zsb3dSdW5JRCI6ICJhMTdjYTIxNi0yOWI2LTQxMDctYjNmYy05MjU2ZjdhZjJmZTYiLCAid29ya2Zsb3dJRCI6ICI1ZTM0YjM0Zi04ODJjLTQ4YjgtODRjMC00ZjIzOGUxNWE1ZmQifSwgImVudiI6IHsib3duZXIiOiAiam9obi1jQGNoYWlubG9vcC5kZXYiLCAicHJvamVjdCI6ICJjaGF0Z3B0In0sICJidWlsZGVyIjogeyJpZCI6ICJjaGFpbmxvb3AuZGV2L2NsaS9kZXZAc2hhMjU2OmE4ZGRkNzM4MTMwMDJmNjJkMmRlMTFhNDIwNTRjMDZmYWUwZDBhODhiNmM4OTA0ZTdkZTQ0NzUxOThjMGIwZDMifSwgImJ1aWxkVHlwZSI6ICJjaGFpbmxvb3AuZGV2L3dvcmtmbG93cnVuL3YwLjEiLCAicnVubmVyVHlwZSI6ICJSVU5ORVJfVFlQRV9VTlNQRUNJRklFRCIsICJhbm5vdGF0aW9ucyI6IHsidG9wbGV2ZWwiOiAidHJ1ZSIsICJicmFuY2giOiAic3RhYmxlIn0sICJtYXRlcmlhbHMiOiBbeyJkaWdlc3QiOiB7InNoYTI1NiI6ICIyNjRmNTVhNmZmOWNlYzJmNDc0MmE5ZmFhY2MwMzNiMjlmNjVjMDRkZDQ0ODBlNzFlMjM1NzlkNDg0Mjg4ZDYxIn0sICJuYW1lIjogImluZGV4LmRvY2tlci5pby9iaXRuYW1pL25naW54IiwgImFubm90YXRpb25zIjogeyJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJpbWFnZSIsICJjaGFpbmxvb3AubWF0ZXJpYWwudHlwZSI6ICJDT05UQUlORVJfSU1BR0UifX0sIHsiZGlnZXN0IjogeyJzaGEyNTYiOiAiMTYxNTliYjg4MWViNGFiN2ViNWQ4YWZjNTM1MGIwZmVlZWQxZTMxYzBhMjY4ZTM1NWU3NGY5Y2NiZTg4NWUwYyJ9LCAibmFtZSI6ICJzYm9tLmN5Y2xvbmVkeC5qc29uIiwgImFubm90YXRpb25zIjogeyJjaGFpbmxvb3AubWF0ZXJpYWwuY2FzIjogdHJ1ZSwgImNoYWlubG9vcC5tYXRlcmlhbC5uYW1lIjogInNreW5ldC1zYm9tIiwgImNoYWlubG9vcC5tYXRlcmlhbC50eXBlIjogIlNCT01fQ1lDTE9ORURYX0pTT04iLCAiY29tcG9uZW50IjogIm5naW54In19LCB7ImRpZ2VzdCI6IHsic2hhMjU2IjogIjE2MTU5YmI4ODFlYjRhYjdlYjVkOGFmYzUzNTBiMGZlZWVkMWUzMWMwYTI2OGUzNTVlNzRmOWNjYmU4ODVlMGMifSwgIm5hbWUiOiAic2JvbS5jeWNsb25lZHguanNvbiIsICJhbm5vdGF0aW9ucyI6IHsiY2hhaW5sb29wLm1hdGVyaWFsLmNhcyI6IHRydWUsICJjaGFpbmxvb3AubWF0ZXJpYWwubmFtZSI6ICJza3luZXQyLXNib20iLCAiY2hhaW5sb29wLm1hdGVyaWFsLnR5cGUiOiAiU0JPTV9DWUNMT05FRFhfSlNPTiJ9fV0sICJwb2xpY3lFdmFsdWF0aW9uc1JlZiI6IHsibmFtZSI6ICJwb2xpY3ktZXZhbHVhdGlvbnMiLCAiZGlnZXN0IjogeyJzaGEyNTYiOiAiYWFiYmNjZGQwMDExMjIzM2FhYmJjY2RkMDAxMTIyMzNhYWJiY2NkZDAwMTEyMjMzYWFiYmNjZGQwMDExMjIzMyJ9LCAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5jaGFpbmxvb3AucG9saWN5LWV2YWx1YXRpb25zLnYxK3Byb3RvIn19fQ==", + "signatures": [ + { + "keyid": "", + "sig": "MEQCIAFytdWto+Bi5Tht+7haXnjiHBfwLwgf6ks0mxeeSadEAiBoLKhy+UKsdDH3ukHJPuiHvWOGhEP19kZ9aipmaT4TlQ==" + } + ] +} diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go index 51603b02f..4962f1c2a 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.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. @@ -152,7 +152,7 @@ func (x Commit_CommitVerification_VerificationStatus) Number() protoreflect.Enum // Deprecated: Use Commit_CommitVerification_VerificationStatus.Descriptor instead. func (Commit_CommitVerification_VerificationStatus) EnumDescriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{3, 1, 0} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{4, 1, 0} } type Attestation struct { @@ -628,6 +628,51 @@ func (x *PolicyEvaluation) GetGate() bool { return false } +// Bundle of all policy evaluations for an attestation, stored as a CAS object. +type PolicyEvaluationBundle struct { + state protoimpl.MessageState `protogen:"open.v1"` + Evaluations []*PolicyEvaluation `protobuf:"bytes,1,rep,name=evaluations,proto3" json:"evaluations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PolicyEvaluationBundle) Reset() { + *x = PolicyEvaluationBundle{} + mi := &file_attestation_v1_crafting_state_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PolicyEvaluationBundle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PolicyEvaluationBundle) ProtoMessage() {} + +func (x *PolicyEvaluationBundle) ProtoReflect() protoreflect.Message { + mi := &file_attestation_v1_crafting_state_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PolicyEvaluationBundle.ProtoReflect.Descriptor instead. +func (*PolicyEvaluationBundle) Descriptor() ([]byte, []int) { + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{3} +} + +func (x *PolicyEvaluationBundle) GetEvaluations() []*PolicyEvaluation { + if x != nil { + return x.Evaluations + } + return nil +} + type Commit struct { state protoimpl.MessageState `protogen:"open.v1"` Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` @@ -646,7 +691,7 @@ type Commit struct { func (x *Commit) Reset() { *x = Commit{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[3] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -658,7 +703,7 @@ func (x *Commit) String() string { func (*Commit) ProtoMessage() {} func (x *Commit) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[3] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -671,7 +716,7 @@ func (x *Commit) ProtoReflect() protoreflect.Message { // Deprecated: Use Commit.ProtoReflect.Descriptor instead. func (*Commit) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{3} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{4} } func (x *Commit) GetHash() string { @@ -748,7 +793,7 @@ type CraftingState struct { func (x *CraftingState) Reset() { *x = CraftingState{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[4] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -760,7 +805,7 @@ func (x *CraftingState) String() string { func (*CraftingState) ProtoMessage() {} func (x *CraftingState) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[4] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -773,7 +818,7 @@ func (x *CraftingState) ProtoReflect() protoreflect.Message { // Deprecated: Use CraftingState.ProtoReflect.Descriptor instead. func (*CraftingState) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{4} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{5} } func (x *CraftingState) GetSchema() isCraftingState_Schema { @@ -862,7 +907,7 @@ type WorkflowMetadata struct { func (x *WorkflowMetadata) Reset() { *x = WorkflowMetadata{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[5] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -874,7 +919,7 @@ func (x *WorkflowMetadata) String() string { func (*WorkflowMetadata) ProtoMessage() {} func (x *WorkflowMetadata) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[5] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -887,7 +932,7 @@ func (x *WorkflowMetadata) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkflowMetadata.ProtoReflect.Descriptor instead. func (*WorkflowMetadata) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{5} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{6} } func (x *WorkflowMetadata) GetName() string { @@ -973,7 +1018,7 @@ type ProjectVersion struct { func (x *ProjectVersion) Reset() { *x = ProjectVersion{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[6] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -985,7 +1030,7 @@ func (x *ProjectVersion) String() string { func (*ProjectVersion) ProtoMessage() {} func (x *ProjectVersion) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[6] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -998,7 +1043,7 @@ func (x *ProjectVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use ProjectVersion.ProtoReflect.Descriptor instead. func (*ProjectVersion) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{6} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{7} } func (x *ProjectVersion) GetVersion() string { @@ -1046,7 +1091,7 @@ type ResourceDescriptor struct { func (x *ResourceDescriptor) Reset() { *x = ResourceDescriptor{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[7] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1058,7 +1103,7 @@ func (x *ResourceDescriptor) String() string { func (*ResourceDescriptor) ProtoMessage() {} func (x *ResourceDescriptor) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[7] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1071,7 +1116,7 @@ func (x *ResourceDescriptor) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceDescriptor.ProtoReflect.Descriptor instead. func (*ResourceDescriptor) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{7} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{8} } func (x *ResourceDescriptor) GetName() string { @@ -1150,7 +1195,7 @@ type Attestation_Material struct { func (x *Attestation_Material) Reset() { *x = Attestation_Material{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[10] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1162,7 +1207,7 @@ func (x *Attestation_Material) String() string { func (*Attestation_Material) ProtoMessage() {} func (x *Attestation_Material) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[10] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1316,7 +1361,7 @@ type Attestation_Auth struct { func (x *Attestation_Auth) Reset() { *x = Attestation_Auth{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[12] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1328,7 +1373,7 @@ func (x *Attestation_Auth) String() string { func (*Attestation_Auth) ProtoMessage() {} func (x *Attestation_Auth) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[12] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1372,7 +1417,7 @@ type Attestation_CASBackend struct { func (x *Attestation_CASBackend) Reset() { *x = Attestation_CASBackend{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[13] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1384,7 +1429,7 @@ func (x *Attestation_CASBackend) String() string { func (*Attestation_CASBackend) ProtoMessage() {} func (x *Attestation_CASBackend) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[13] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1433,7 +1478,7 @@ type Attestation_SigningOptions struct { func (x *Attestation_SigningOptions) Reset() { *x = Attestation_SigningOptions{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[14] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1445,7 +1490,7 @@ func (x *Attestation_SigningOptions) String() string { func (*Attestation_SigningOptions) ProtoMessage() {} func (x *Attestation_SigningOptions) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[14] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1490,7 +1535,7 @@ type Attestation_Material_KeyVal struct { func (x *Attestation_Material_KeyVal) Reset() { *x = Attestation_Material_KeyVal{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[16] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1502,7 +1547,7 @@ func (x *Attestation_Material_KeyVal) String() string { func (*Attestation_Material_KeyVal) ProtoMessage() {} func (x *Attestation_Material_KeyVal) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[16] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1568,7 +1613,7 @@ type Attestation_Material_ContainerImage struct { func (x *Attestation_Material_ContainerImage) Reset() { *x = Attestation_Material_ContainerImage{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[17] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1580,7 +1625,7 @@ func (x *Attestation_Material_ContainerImage) String() string { func (*Attestation_Material_ContainerImage) ProtoMessage() {} func (x *Attestation_Material_ContainerImage) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[17] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1682,7 +1727,7 @@ type Attestation_Material_Artifact struct { func (x *Attestation_Material_Artifact) Reset() { *x = Attestation_Material_Artifact{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[18] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1694,7 +1739,7 @@ func (x *Attestation_Material_Artifact) String() string { func (*Attestation_Material_Artifact) ProtoMessage() {} func (x *Attestation_Material_Artifact) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[18] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1758,7 +1803,7 @@ type Attestation_Material_SBOMArtifact struct { func (x *Attestation_Material_SBOMArtifact) Reset() { *x = Attestation_Material_SBOMArtifact{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[19] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1770,7 +1815,7 @@ func (x *Attestation_Material_SBOMArtifact) String() string { func (*Attestation_Material_SBOMArtifact) ProtoMessage() {} func (x *Attestation_Material_SBOMArtifact) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[19] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1815,7 +1860,7 @@ type Attestation_Material_SBOMArtifact_MainComponent struct { func (x *Attestation_Material_SBOMArtifact_MainComponent) Reset() { *x = Attestation_Material_SBOMArtifact_MainComponent{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[20] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1827,7 +1872,7 @@ func (x *Attestation_Material_SBOMArtifact_MainComponent) String() string { func (*Attestation_Material_SBOMArtifact_MainComponent) ProtoMessage() {} func (x *Attestation_Material_SBOMArtifact_MainComponent) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[20] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1874,7 +1919,7 @@ type PolicyEvaluation_Violation struct { func (x *PolicyEvaluation_Violation) Reset() { *x = PolicyEvaluation_Violation{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[23] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1886,7 +1931,7 @@ func (x *PolicyEvaluation_Violation) String() string { func (*PolicyEvaluation_Violation) ProtoMessage() {} func (x *PolicyEvaluation_Violation) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[23] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1928,7 +1973,7 @@ type PolicyEvaluation_Reference struct { func (x *PolicyEvaluation_Reference) Reset() { *x = PolicyEvaluation_Reference{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[24] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1940,7 +1985,7 @@ func (x *PolicyEvaluation_Reference) String() string { func (*PolicyEvaluation_Reference) ProtoMessage() {} func (x *PolicyEvaluation_Reference) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[24] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1996,7 +2041,7 @@ type PolicyEvaluation_RawResult struct { func (x *PolicyEvaluation_RawResult) Reset() { *x = PolicyEvaluation_RawResult{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[25] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2008,7 +2053,7 @@ func (x *PolicyEvaluation_RawResult) String() string { func (*PolicyEvaluation_RawResult) ProtoMessage() {} func (x *PolicyEvaluation_RawResult) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[25] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2048,7 +2093,7 @@ type Commit_Remote struct { func (x *Commit_Remote) Reset() { *x = Commit_Remote{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[26] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2060,7 +2105,7 @@ func (x *Commit_Remote) String() string { func (*Commit_Remote) ProtoMessage() {} func (x *Commit_Remote) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[26] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2073,7 +2118,7 @@ func (x *Commit_Remote) ProtoReflect() protoreflect.Message { // Deprecated: Use Commit_Remote.ProtoReflect.Descriptor instead. func (*Commit_Remote) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{3, 0} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{4, 0} } func (x *Commit_Remote) GetName() string { @@ -2110,7 +2155,7 @@ type Commit_CommitVerification struct { func (x *Commit_CommitVerification) Reset() { *x = Commit_CommitVerification{} - mi := &file_attestation_v1_crafting_state_proto_msgTypes[27] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2122,7 +2167,7 @@ func (x *Commit_CommitVerification) String() string { func (*Commit_CommitVerification) ProtoMessage() {} func (x *Commit_CommitVerification) ProtoReflect() protoreflect.Message { - mi := &file_attestation_v1_crafting_state_proto_msgTypes[27] + mi := &file_attestation_v1_crafting_state_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2135,7 +2180,7 @@ func (x *Commit_CommitVerification) ProtoReflect() protoreflect.Message { // Deprecated: Use Commit_CommitVerification.ProtoReflect.Descriptor instead. func (*Commit_CommitVerification) Descriptor() ([]byte, []int) { - return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{3, 1} + return file_attestation_v1_crafting_state_proto_rawDescGZIP(), []int{4, 1} } func (x *Commit_CommitVerification) GetAttempted() bool { @@ -2329,7 +2374,9 @@ const file_attestation_v1_crafting_state_proto_rawDesc = "" + "\borg_name\x18\x04 \x01(\tR\aorgName\x1a9\n" + "\tRawResult\x12\x14\n" + "\x05input\x18\x01 \x01(\fR\x05input\x12\x16\n" + - "\x06output\x18\x02 \x01(\fR\x06output\"\xce\x06\n" + + "\x06output\x18\x02 \x01(\fR\x06output\"\\\n" + + "\x16PolicyEvaluationBundle\x12B\n" + + "\vevaluations\x18\x01 \x03(\v2 .attestation.v1.PolicyEvaluationR\vevaluations\"\xce\x06\n" + "\x06Commit\x12\x1b\n" + "\x04hash\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x01R\x04hash\x12!\n" + "\fauthor_email\x18\x02 \x01(\tR\vauthorEmail\x12(\n" + @@ -2410,96 +2457,98 @@ func file_attestation_v1_crafting_state_proto_rawDescGZIP() []byte { } var file_attestation_v1_crafting_state_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_attestation_v1_crafting_state_proto_msgTypes = make([]protoimpl.MessageInfo, 29) +var file_attestation_v1_crafting_state_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_attestation_v1_crafting_state_proto_goTypes = []any{ (Attestation_Auth_AuthType)(0), // 0: attestation.v1.Attestation.Auth.AuthType (Commit_CommitVerification_VerificationStatus)(0), // 1: attestation.v1.Commit.CommitVerification.VerificationStatus (*Attestation)(nil), // 2: attestation.v1.Attestation (*RunnerEnvironment)(nil), // 3: attestation.v1.RunnerEnvironment (*PolicyEvaluation)(nil), // 4: attestation.v1.PolicyEvaluation - (*Commit)(nil), // 5: attestation.v1.Commit - (*CraftingState)(nil), // 6: attestation.v1.CraftingState - (*WorkflowMetadata)(nil), // 7: attestation.v1.WorkflowMetadata - (*ProjectVersion)(nil), // 8: attestation.v1.ProjectVersion - (*ResourceDescriptor)(nil), // 9: attestation.v1.ResourceDescriptor - nil, // 10: attestation.v1.Attestation.MaterialsEntry - nil, // 11: attestation.v1.Attestation.AnnotationsEntry - (*Attestation_Material)(nil), // 12: attestation.v1.Attestation.Material - nil, // 13: attestation.v1.Attestation.EnvVarsEntry - (*Attestation_Auth)(nil), // 14: attestation.v1.Attestation.Auth - (*Attestation_CASBackend)(nil), // 15: attestation.v1.Attestation.CASBackend - (*Attestation_SigningOptions)(nil), // 16: attestation.v1.Attestation.SigningOptions - nil, // 17: attestation.v1.Attestation.Material.AnnotationsEntry - (*Attestation_Material_KeyVal)(nil), // 18: attestation.v1.Attestation.Material.KeyVal - (*Attestation_Material_ContainerImage)(nil), // 19: attestation.v1.Attestation.Material.ContainerImage - (*Attestation_Material_Artifact)(nil), // 20: attestation.v1.Attestation.Material.Artifact - (*Attestation_Material_SBOMArtifact)(nil), // 21: attestation.v1.Attestation.Material.SBOMArtifact - (*Attestation_Material_SBOMArtifact_MainComponent)(nil), // 22: attestation.v1.Attestation.Material.SBOMArtifact.MainComponent - nil, // 23: attestation.v1.PolicyEvaluation.AnnotationsEntry - nil, // 24: attestation.v1.PolicyEvaluation.WithEntry - (*PolicyEvaluation_Violation)(nil), // 25: attestation.v1.PolicyEvaluation.Violation - (*PolicyEvaluation_Reference)(nil), // 26: attestation.v1.PolicyEvaluation.Reference - (*PolicyEvaluation_RawResult)(nil), // 27: attestation.v1.PolicyEvaluation.RawResult - (*Commit_Remote)(nil), // 28: attestation.v1.Commit.Remote - (*Commit_CommitVerification)(nil), // 29: attestation.v1.Commit.CommitVerification - nil, // 30: attestation.v1.ResourceDescriptor.DigestEntry - (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp - (v1.CraftingSchema_Runner_RunnerType)(0), // 32: workflowcontract.v1.CraftingSchema.Runner.RunnerType - (v1.CraftingSchema_Material_MaterialType)(0), // 33: workflowcontract.v1.CraftingSchema.Material.MaterialType - (*v1.CraftingSchema)(nil), // 34: workflowcontract.v1.CraftingSchema - (*v1.CraftingSchemaV2)(nil), // 35: workflowcontract.v1.CraftingSchemaV2 - (*structpb.Struct)(nil), // 36: google.protobuf.Struct - (*wrapperspb.BoolValue)(nil), // 37: google.protobuf.BoolValue + (*PolicyEvaluationBundle)(nil), // 5: attestation.v1.PolicyEvaluationBundle + (*Commit)(nil), // 6: attestation.v1.Commit + (*CraftingState)(nil), // 7: attestation.v1.CraftingState + (*WorkflowMetadata)(nil), // 8: attestation.v1.WorkflowMetadata + (*ProjectVersion)(nil), // 9: attestation.v1.ProjectVersion + (*ResourceDescriptor)(nil), // 10: attestation.v1.ResourceDescriptor + nil, // 11: attestation.v1.Attestation.MaterialsEntry + nil, // 12: attestation.v1.Attestation.AnnotationsEntry + (*Attestation_Material)(nil), // 13: attestation.v1.Attestation.Material + nil, // 14: attestation.v1.Attestation.EnvVarsEntry + (*Attestation_Auth)(nil), // 15: attestation.v1.Attestation.Auth + (*Attestation_CASBackend)(nil), // 16: attestation.v1.Attestation.CASBackend + (*Attestation_SigningOptions)(nil), // 17: attestation.v1.Attestation.SigningOptions + nil, // 18: attestation.v1.Attestation.Material.AnnotationsEntry + (*Attestation_Material_KeyVal)(nil), // 19: attestation.v1.Attestation.Material.KeyVal + (*Attestation_Material_ContainerImage)(nil), // 20: attestation.v1.Attestation.Material.ContainerImage + (*Attestation_Material_Artifact)(nil), // 21: attestation.v1.Attestation.Material.Artifact + (*Attestation_Material_SBOMArtifact)(nil), // 22: attestation.v1.Attestation.Material.SBOMArtifact + (*Attestation_Material_SBOMArtifact_MainComponent)(nil), // 23: attestation.v1.Attestation.Material.SBOMArtifact.MainComponent + nil, // 24: attestation.v1.PolicyEvaluation.AnnotationsEntry + nil, // 25: attestation.v1.PolicyEvaluation.WithEntry + (*PolicyEvaluation_Violation)(nil), // 26: attestation.v1.PolicyEvaluation.Violation + (*PolicyEvaluation_Reference)(nil), // 27: attestation.v1.PolicyEvaluation.Reference + (*PolicyEvaluation_RawResult)(nil), // 28: attestation.v1.PolicyEvaluation.RawResult + (*Commit_Remote)(nil), // 29: attestation.v1.Commit.Remote + (*Commit_CommitVerification)(nil), // 30: attestation.v1.Commit.CommitVerification + nil, // 31: attestation.v1.ResourceDescriptor.DigestEntry + (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp + (v1.CraftingSchema_Runner_RunnerType)(0), // 33: workflowcontract.v1.CraftingSchema.Runner.RunnerType + (v1.CraftingSchema_Material_MaterialType)(0), // 34: workflowcontract.v1.CraftingSchema.Material.MaterialType + (*v1.CraftingSchema)(nil), // 35: workflowcontract.v1.CraftingSchema + (*v1.CraftingSchemaV2)(nil), // 36: workflowcontract.v1.CraftingSchemaV2 + (*structpb.Struct)(nil), // 37: google.protobuf.Struct + (*wrapperspb.BoolValue)(nil), // 38: google.protobuf.BoolValue } var file_attestation_v1_crafting_state_proto_depIdxs = []int32{ - 31, // 0: attestation.v1.Attestation.initialized_at:type_name -> google.protobuf.Timestamp - 31, // 1: attestation.v1.Attestation.finished_at:type_name -> google.protobuf.Timestamp - 7, // 2: attestation.v1.Attestation.workflow:type_name -> attestation.v1.WorkflowMetadata - 10, // 3: attestation.v1.Attestation.materials:type_name -> attestation.v1.Attestation.MaterialsEntry - 11, // 4: attestation.v1.Attestation.annotations:type_name -> attestation.v1.Attestation.AnnotationsEntry - 13, // 5: attestation.v1.Attestation.env_vars:type_name -> attestation.v1.Attestation.EnvVarsEntry - 32, // 6: attestation.v1.Attestation.runner_type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType - 5, // 7: attestation.v1.Attestation.head:type_name -> attestation.v1.Commit + 32, // 0: attestation.v1.Attestation.initialized_at:type_name -> google.protobuf.Timestamp + 32, // 1: attestation.v1.Attestation.finished_at:type_name -> google.protobuf.Timestamp + 8, // 2: attestation.v1.Attestation.workflow:type_name -> attestation.v1.WorkflowMetadata + 11, // 3: attestation.v1.Attestation.materials:type_name -> attestation.v1.Attestation.MaterialsEntry + 12, // 4: attestation.v1.Attestation.annotations:type_name -> attestation.v1.Attestation.AnnotationsEntry + 14, // 5: attestation.v1.Attestation.env_vars:type_name -> attestation.v1.Attestation.EnvVarsEntry + 33, // 6: attestation.v1.Attestation.runner_type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType + 6, // 7: attestation.v1.Attestation.head:type_name -> attestation.v1.Commit 4, // 8: attestation.v1.Attestation.policy_evaluations:type_name -> attestation.v1.PolicyEvaluation - 16, // 9: attestation.v1.Attestation.signing_options:type_name -> attestation.v1.Attestation.SigningOptions + 17, // 9: attestation.v1.Attestation.signing_options:type_name -> attestation.v1.Attestation.SigningOptions 3, // 10: attestation.v1.Attestation.runner_environment:type_name -> attestation.v1.RunnerEnvironment - 14, // 11: attestation.v1.Attestation.auth:type_name -> attestation.v1.Attestation.Auth - 15, // 12: attestation.v1.Attestation.cas_backend:type_name -> attestation.v1.Attestation.CASBackend - 32, // 13: attestation.v1.RunnerEnvironment.type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType - 23, // 14: attestation.v1.PolicyEvaluation.annotations:type_name -> attestation.v1.PolicyEvaluation.AnnotationsEntry - 25, // 15: attestation.v1.PolicyEvaluation.violations:type_name -> attestation.v1.PolicyEvaluation.Violation - 24, // 16: attestation.v1.PolicyEvaluation.with:type_name -> attestation.v1.PolicyEvaluation.WithEntry - 33, // 17: attestation.v1.PolicyEvaluation.type:type_name -> workflowcontract.v1.CraftingSchema.Material.MaterialType - 26, // 18: attestation.v1.PolicyEvaluation.policy_reference:type_name -> attestation.v1.PolicyEvaluation.Reference - 26, // 19: attestation.v1.PolicyEvaluation.group_reference:type_name -> attestation.v1.PolicyEvaluation.Reference - 27, // 20: attestation.v1.PolicyEvaluation.raw_results:type_name -> attestation.v1.PolicyEvaluation.RawResult - 31, // 21: attestation.v1.Commit.date:type_name -> google.protobuf.Timestamp - 28, // 22: attestation.v1.Commit.remotes:type_name -> attestation.v1.Commit.Remote - 29, // 23: attestation.v1.Commit.platform_verification:type_name -> attestation.v1.Commit.CommitVerification - 34, // 24: attestation.v1.CraftingState.input_schema:type_name -> workflowcontract.v1.CraftingSchema - 35, // 25: attestation.v1.CraftingState.schema_v2:type_name -> workflowcontract.v1.CraftingSchemaV2 - 2, // 26: attestation.v1.CraftingState.attestation:type_name -> attestation.v1.Attestation - 8, // 27: attestation.v1.WorkflowMetadata.version:type_name -> attestation.v1.ProjectVersion - 30, // 28: attestation.v1.ResourceDescriptor.digest:type_name -> attestation.v1.ResourceDescriptor.DigestEntry - 36, // 29: attestation.v1.ResourceDescriptor.annotations:type_name -> google.protobuf.Struct - 12, // 30: attestation.v1.Attestation.MaterialsEntry.value:type_name -> attestation.v1.Attestation.Material - 18, // 31: attestation.v1.Attestation.Material.string:type_name -> attestation.v1.Attestation.Material.KeyVal - 19, // 32: attestation.v1.Attestation.Material.container_image:type_name -> attestation.v1.Attestation.Material.ContainerImage - 20, // 33: attestation.v1.Attestation.Material.artifact:type_name -> attestation.v1.Attestation.Material.Artifact - 21, // 34: attestation.v1.Attestation.Material.sbom_artifact:type_name -> attestation.v1.Attestation.Material.SBOMArtifact - 31, // 35: attestation.v1.Attestation.Material.added_at:type_name -> google.protobuf.Timestamp - 33, // 36: attestation.v1.Attestation.Material.material_type:type_name -> workflowcontract.v1.CraftingSchema.Material.MaterialType - 17, // 37: attestation.v1.Attestation.Material.annotations:type_name -> attestation.v1.Attestation.Material.AnnotationsEntry - 0, // 38: attestation.v1.Attestation.Auth.type:type_name -> attestation.v1.Attestation.Auth.AuthType - 37, // 39: attestation.v1.Attestation.Material.ContainerImage.has_latest_tag:type_name -> google.protobuf.BoolValue - 20, // 40: attestation.v1.Attestation.Material.SBOMArtifact.artifact:type_name -> attestation.v1.Attestation.Material.Artifact - 22, // 41: attestation.v1.Attestation.Material.SBOMArtifact.main_component:type_name -> attestation.v1.Attestation.Material.SBOMArtifact.MainComponent - 1, // 42: attestation.v1.Commit.CommitVerification.status:type_name -> attestation.v1.Commit.CommitVerification.VerificationStatus - 43, // [43:43] is the sub-list for method output_type - 43, // [43:43] is the sub-list for method input_type - 43, // [43:43] is the sub-list for extension type_name - 43, // [43:43] is the sub-list for extension extendee - 0, // [0:43] is the sub-list for field type_name + 15, // 11: attestation.v1.Attestation.auth:type_name -> attestation.v1.Attestation.Auth + 16, // 12: attestation.v1.Attestation.cas_backend:type_name -> attestation.v1.Attestation.CASBackend + 33, // 13: attestation.v1.RunnerEnvironment.type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType + 24, // 14: attestation.v1.PolicyEvaluation.annotations:type_name -> attestation.v1.PolicyEvaluation.AnnotationsEntry + 26, // 15: attestation.v1.PolicyEvaluation.violations:type_name -> attestation.v1.PolicyEvaluation.Violation + 25, // 16: attestation.v1.PolicyEvaluation.with:type_name -> attestation.v1.PolicyEvaluation.WithEntry + 34, // 17: attestation.v1.PolicyEvaluation.type:type_name -> workflowcontract.v1.CraftingSchema.Material.MaterialType + 27, // 18: attestation.v1.PolicyEvaluation.policy_reference:type_name -> attestation.v1.PolicyEvaluation.Reference + 27, // 19: attestation.v1.PolicyEvaluation.group_reference:type_name -> attestation.v1.PolicyEvaluation.Reference + 28, // 20: attestation.v1.PolicyEvaluation.raw_results:type_name -> attestation.v1.PolicyEvaluation.RawResult + 4, // 21: attestation.v1.PolicyEvaluationBundle.evaluations:type_name -> attestation.v1.PolicyEvaluation + 32, // 22: attestation.v1.Commit.date:type_name -> google.protobuf.Timestamp + 29, // 23: attestation.v1.Commit.remotes:type_name -> attestation.v1.Commit.Remote + 30, // 24: attestation.v1.Commit.platform_verification:type_name -> attestation.v1.Commit.CommitVerification + 35, // 25: attestation.v1.CraftingState.input_schema:type_name -> workflowcontract.v1.CraftingSchema + 36, // 26: attestation.v1.CraftingState.schema_v2:type_name -> workflowcontract.v1.CraftingSchemaV2 + 2, // 27: attestation.v1.CraftingState.attestation:type_name -> attestation.v1.Attestation + 9, // 28: attestation.v1.WorkflowMetadata.version:type_name -> attestation.v1.ProjectVersion + 31, // 29: attestation.v1.ResourceDescriptor.digest:type_name -> attestation.v1.ResourceDescriptor.DigestEntry + 37, // 30: attestation.v1.ResourceDescriptor.annotations:type_name -> google.protobuf.Struct + 13, // 31: attestation.v1.Attestation.MaterialsEntry.value:type_name -> attestation.v1.Attestation.Material + 19, // 32: attestation.v1.Attestation.Material.string:type_name -> attestation.v1.Attestation.Material.KeyVal + 20, // 33: attestation.v1.Attestation.Material.container_image:type_name -> attestation.v1.Attestation.Material.ContainerImage + 21, // 34: attestation.v1.Attestation.Material.artifact:type_name -> attestation.v1.Attestation.Material.Artifact + 22, // 35: attestation.v1.Attestation.Material.sbom_artifact:type_name -> attestation.v1.Attestation.Material.SBOMArtifact + 32, // 36: attestation.v1.Attestation.Material.added_at:type_name -> google.protobuf.Timestamp + 34, // 37: attestation.v1.Attestation.Material.material_type:type_name -> workflowcontract.v1.CraftingSchema.Material.MaterialType + 18, // 38: attestation.v1.Attestation.Material.annotations:type_name -> attestation.v1.Attestation.Material.AnnotationsEntry + 0, // 39: attestation.v1.Attestation.Auth.type:type_name -> attestation.v1.Attestation.Auth.AuthType + 38, // 40: attestation.v1.Attestation.Material.ContainerImage.has_latest_tag:type_name -> google.protobuf.BoolValue + 21, // 41: attestation.v1.Attestation.Material.SBOMArtifact.artifact:type_name -> attestation.v1.Attestation.Material.Artifact + 23, // 42: attestation.v1.Attestation.Material.SBOMArtifact.main_component:type_name -> attestation.v1.Attestation.Material.SBOMArtifact.MainComponent + 1, // 43: attestation.v1.Commit.CommitVerification.status:type_name -> attestation.v1.Commit.CommitVerification.VerificationStatus + 44, // [44:44] is the sub-list for method output_type + 44, // [44:44] is the sub-list for method input_type + 44, // [44:44] is the sub-list for extension type_name + 44, // [44:44] is the sub-list for extension extendee + 0, // [0:44] is the sub-list for field type_name } func init() { file_attestation_v1_crafting_state_proto_init() } @@ -2507,12 +2556,12 @@ func file_attestation_v1_crafting_state_proto_init() { if File_attestation_v1_crafting_state_proto != nil { return } - file_attestation_v1_crafting_state_proto_msgTypes[3].OneofWrappers = []any{} - file_attestation_v1_crafting_state_proto_msgTypes[4].OneofWrappers = []any{ + file_attestation_v1_crafting_state_proto_msgTypes[4].OneofWrappers = []any{} + file_attestation_v1_crafting_state_proto_msgTypes[5].OneofWrappers = []any{ (*CraftingState_InputSchema)(nil), (*CraftingState_SchemaV2)(nil), } - file_attestation_v1_crafting_state_proto_msgTypes[10].OneofWrappers = []any{ + file_attestation_v1_crafting_state_proto_msgTypes[11].OneofWrappers = []any{ (*Attestation_Material_String_)(nil), (*Attestation_Material_ContainerImage_)(nil), (*Attestation_Material_Artifact_)(nil), @@ -2524,7 +2573,7 @@ func file_attestation_v1_crafting_state_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_attestation_v1_crafting_state_proto_rawDesc), len(file_attestation_v1_crafting_state_proto_rawDesc)), NumEnums: 2, - NumMessages: 29, + NumMessages: 30, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto b/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto index ba3c9f89d..4791b270f 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto @@ -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. @@ -298,6 +298,11 @@ message PolicyEvaluation { } } +// Bundle of all policy evaluations for an attestation, stored as a CAS object. +message PolicyEvaluationBundle { + repeated PolicyEvaluation evaluations = 1; +} + message Commit { string hash = 1 [(buf.validate.field).string.min_len = 1]; // Commit authors might not include email i.e "Flux <>" diff --git a/pkg/attestation/renderer/chainloop/chainloop.go b/pkg/attestation/renderer/chainloop/chainloop.go index 53c1490d9..a5bebe019 100644 --- a/pkg/attestation/renderer/chainloop/chainloop.go +++ b/pkg/attestation/renderer/chainloop/chainloop.go @@ -42,6 +42,7 @@ type NormalizablePredicate interface { GetRunLink() string GetMetadata() *Metadata GetPolicyEvaluations() map[string][]*PolicyEvaluation + GetPolicyEvaluationsRef() *intoto.ResourceDescriptor GetPolicyEvaluationStatus() *PolicyEvaluationStatus } diff --git a/pkg/attestation/renderer/chainloop/v02.go b/pkg/attestation/renderer/chainloop/v02.go index 41bc657c8..44cdb3f73 100644 --- a/pkg/attestation/renderer/chainloop/v02.go +++ b/pkg/attestation/renderer/chainloop/v02.go @@ -38,12 +38,15 @@ import ( // Replace custom material type with https://github.com/in-toto/attestation/blob/main/spec/v1.0/resource_descriptor.md const PredicateTypeV02 = "chainloop.dev/attestation/v0.2" const AttPolicyEvaluation = "CHAINLOOP.ATTESTATION" +const PolicyEvaluationsBundleMediaType = "application/vnd.chainloop.policy-evaluations.v1+json" type ProvenancePredicateV02 struct { *ProvenancePredicateCommon Materials []*intoto.ResourceDescriptor `json:"materials,omitempty"` - // Map materials and policies + // Deprecated: use PolicyEvaluationsRef to fetch full data from CAS. PolicyEvaluations map[string][]*PolicyEvaluation `json:"policyEvaluations,omitempty"` + // Reference to the PolicyEvaluationBundle stored in CAS + PolicyEvaluationsRef *intoto.ResourceDescriptor `json:"policyEvaluationsRef,omitempty"` // Used to read policy evaluations from old attestations PolicyEvaluationsFallback map[string][]*PolicyEvaluation `json:"policy_evaluations,omitempty"` @@ -99,17 +102,25 @@ type PolicyViolation struct { type RendererV02 struct { *RendererCommon - attClient pb.AttestationServiceClient - logger *zerolog.Logger + attClient pb.AttestationServiceClient + logger *zerolog.Logger + policyEvaluationsRef *intoto.ResourceDescriptor +} + +// SetPolicyEvaluationsRef sets the CAS reference for the policy evaluations bundle. +func (r *RendererV02) SetPolicyEvaluationsRef(ref *intoto.ResourceDescriptor) { + r.policyEvaluationsRef = ref } func NewChainloopRendererV02(att *v1.Attestation, builderVersion, builderDigest string, attClient pb.AttestationServiceClient, logger *zerolog.Logger) *RendererV02 { return &RendererV02{ - &RendererCommon{ - PredicateTypeV02, att, &builderInfo{builderVersion, builderDigest}, + RendererCommon: &RendererCommon{ + predicateType: PredicateTypeV02, + att: att, + builder: &builderInfo{builderVersion, builderDigest}, }, - attClient, - logger, + attClient: attClient, + logger: logger, } } @@ -251,6 +262,7 @@ func (r *RendererV02) predicate() (*structpb.Struct, error) { ProvenancePredicateCommon: predicateCommon(r.builder, r.att), Materials: normalizedMaterials, PolicyEvaluations: policies, + PolicyEvaluationsRef: r.policyEvaluationsRef, PolicyHasViolations: hasViolations, PolicyHasGatedViolations: gated, PolicyCheckBlockingStrategy: policyCheckBlockingStrategy, @@ -416,6 +428,10 @@ func (p *ProvenancePredicateV02) GetPolicyEvaluations() map[string][]*PolicyEval return p.PolicyEvaluations } +func (p *ProvenancePredicateV02) GetPolicyEvaluationsRef() *intoto.ResourceDescriptor { + return p.PolicyEvaluationsRef +} + func (p *ProvenancePredicateV02) GetPolicyEvaluationStatus() *PolicyEvaluationStatus { return &PolicyEvaluationStatus{ Strategy: p.PolicyCheckBlockingStrategy, diff --git a/pkg/attestation/renderer/chainloop/v02_test.go b/pkg/attestation/renderer/chainloop/v02_test.go index d867170a6..73f0eff28 100644 --- a/pkg/attestation/renderer/chainloop/v02_test.go +++ b/pkg/attestation/renderer/chainloop/v02_test.go @@ -338,6 +338,63 @@ func mustStructValue(t *testing.T, fields map[string]any) *structpb.Value { return structpb.NewStructValue(s) } +func TestPredicatePolicyEvaluationsRef(t *testing.T) { + testCases := []struct { + name string + ref *intoto.ResourceDescriptor + wantRef bool + }{ + { + name: "ref is present when set", + ref: &intoto.ResourceDescriptor{ + Name: "policy-evaluations", + Digest: map[string]string{"sha256": "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"}, + MediaType: PolicyEvaluationsBundleMediaType, + }, + wantRef: true, + }, + { + name: "ref is nil when not set", + ref: nil, + wantRef: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + stateRaw, err := os.ReadFile("testdata/attestation.source.v2.json") + require.NoError(t, err) + + state := &api.CraftingState{} + err = protojson.Unmarshal(stateRaw, state) + require.NoError(t, err) + + renderer := NewChainloopRendererV02(state.Attestation, "dev", "sha256:59e14f1a9de709cdd0e91c36b33e54fcca95f7dba1dc7169a7f81986e02108e5", nil, nil) + + if tc.ref != nil { + renderer.SetPolicyEvaluationsRef(tc.ref) + } + + statement, err := renderer.Statement(context.TODO()) + require.NoError(t, err) + + var predicate ProvenancePredicateV02 + err = extractPredicate(statement, &predicate) + require.NoError(t, err) + + if !tc.wantRef { + assert.Nil(t, predicate.PolicyEvaluationsRef) + return + } + + require.NotNil(t, predicate.PolicyEvaluationsRef) + 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"]) + }) + } +} + func TestPolicyEvaluationsField(t *testing.T) { raw, err := os.ReadFile("testdata/attestation-pe-snake.json") require.NoError(t, err) diff --git a/pkg/attestation/renderer/renderer.go b/pkg/attestation/renderer/renderer.go index a38dfc333..644578010 100644 --- a/pkg/attestation/renderer/renderer.go +++ b/pkg/attestation/renderer/renderer.go @@ -1,5 +1,5 @@ // -// Copyright 2023 The Chainloop Authors. +// Copyright 2023-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. @@ -42,13 +42,14 @@ import ( ) type AttestationRenderer struct { - logger zerolog.Logger - att *v1.Attestation - renderer r - signer sigstoresigner.Signer - dsseSigner sigstoresigner.Signer - bundlePath string - attClient pb.AttestationServiceClient + logger zerolog.Logger + att *v1.Attestation + renderer r + v02Renderer *chainloop.RendererV02 + signer sigstoresigner.Signer + dsseSigner sigstoresigner.Signer + bundlePath string + attClient pb.AttestationServiceClient } type r interface { @@ -86,7 +87,9 @@ func NewAttestationRenderer(state *crafter.VersionedCraftingState, attClient pb. opt(r) } - r.renderer = chainloop.NewChainloopRendererV02(state.GetAttestation(), builderVersion, builderDigest, attClient, &r.logger) + v02 := chainloop.NewChainloopRendererV02(state.GetAttestation(), builderVersion, builderDigest, attClient, &r.logger) + r.renderer = v02 + r.v02Renderer = v02 return r, nil } @@ -101,6 +104,12 @@ func (ab *AttestationRenderer) RenderStatement(ctx context.Context) (*intoto.Sta return statement, nil } +// SetPolicyEvaluationsRef sets the CAS reference for the policy evaluations bundle +// on the underlying renderer. This must be called before Render(). +func (ab *AttestationRenderer) SetPolicyEvaluationsRef(ref *intoto.ResourceDescriptor) { + ab.v02Renderer.SetPolicyEvaluationsRef(ref) +} + // Attestation (dsee envelope) -> { message: { Statement(in-toto): [subject, predicate] }, signature: "sig" }. // NOTE: It currently only supports cosign key based signing. func (ab *AttestationRenderer) Render(ctx context.Context) (*dsse.Envelope, *protobundle.Bundle, error) {