11//
2- // Copyright 2024-2025 The Chainloop Authors.
2+ // Copyright 2024-2026 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
1616package service
1717
1818import (
19+ "bytes"
1920 "context"
2021 "fmt"
2122 "slices"
@@ -25,9 +26,12 @@ import (
2526 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz"
2627 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz"
2728 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination"
29+ chainloop "github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop"
30+ "github.com/chainloop-dev/chainloop/pkg/cache"
2831 "github.com/chainloop-dev/chainloop/pkg/credentials"
2932 errors "github.com/go-kratos/kratos/v2/errors"
3033 "github.com/google/uuid"
34+ intoto "github.com/in-toto/attestation/go/v1"
3135 "google.golang.org/protobuf/types/known/timestamppb"
3236)
3337
@@ -40,6 +44,9 @@ type WorkflowRunService struct {
4044 workflowContractUseCase * biz.WorkflowContractUseCase
4145 projectUseCase * biz.ProjectUseCase
4246 credsReader credentials.Reader
47+ casClient biz.CASClient
48+ casMappingUC * biz.CASMappingUseCase
49+ policyEvalCache cache.Cache [[]byte ]
4350}
4451
4552type NewWorkflowRunServiceOpts struct {
@@ -48,6 +55,9 @@ type NewWorkflowRunServiceOpts struct {
4855 WorkflowContractUC * biz.WorkflowContractUseCase
4956 ProjectUC * biz.ProjectUseCase
5057 CredsReader credentials.Reader
58+ CASClient biz.CASClient
59+ CASMappingUC * biz.CASMappingUseCase
60+ PolicyEvalCache cache.Cache [[]byte ]
5161 Opts []NewOpt
5262}
5363
@@ -59,9 +69,56 @@ func NewWorkflowRunService(opts *NewWorkflowRunServiceOpts) *WorkflowRunService
5969 workflowContractUseCase : opts .WorkflowContractUC ,
6070 projectUseCase : opts .ProjectUC ,
6171 credsReader : opts .CredsReader ,
72+ casClient : opts .CASClient ,
73+ casMappingUC : opts .CASMappingUC ,
74+ policyEvalCache : opts .PolicyEvalCache ,
6275 }
6376}
6477
78+ type casResolvedPredicate struct {
79+ chainloop.NormalizablePredicate
80+ evals map [string ][]* chainloop.PolicyEvaluation
81+ }
82+
83+ func (p * casResolvedPredicate ) GetPolicyEvaluations () map [string ][]* chainloop.PolicyEvaluation {
84+ return p .evals
85+ }
86+
87+ func (s * WorkflowRunService ) resolvePolicyEvaluations (
88+ ctx context.Context ,
89+ ref * intoto.ResourceDescriptor ,
90+ orgID uuid.UUID ,
91+ ) (map [string ][]* chainloop.PolicyEvaluation , error ) {
92+ if ref == nil {
93+ return nil , nil
94+ }
95+
96+ hexDigest , ok := ref .Digest ["sha256" ]
97+ if ! ok {
98+ return nil , fmt .Errorf ("no sha256 digest in policy evaluations ref" )
99+ }
100+ digest := fmt .Sprintf ("sha256:%s" , hexDigest )
101+
102+ if cached , found , err := s .policyEvalCache .Get (ctx , digest ); err == nil && found {
103+ return chainloop .PolicyEvaluationsFromBundle (cached )
104+ }
105+
106+ mapping , err := s .casMappingUC .FindCASMappingForDownloadByOrg (ctx , digest , []uuid.UUID {orgID }, nil )
107+ if err != nil {
108+ return nil , fmt .Errorf ("finding CAS mapping: %w" , err )
109+ }
110+
111+ var buf bytes.Buffer
112+ if err := s .casClient .Download (ctx , string (mapping .CASBackend .Provider ), mapping .CASBackend .SecretName , & buf , digest ); err != nil {
113+ return nil , fmt .Errorf ("downloading policy eval bundle: %w" , err )
114+ }
115+
116+ data := buf .Bytes ()
117+ _ = s .policyEvalCache .Set (ctx , digest , data )
118+
119+ return chainloop .PolicyEvaluationsFromBundle (data )
120+ }
121+
65122func (s * WorkflowRunService ) List (ctx context.Context , req * pb.WorkflowRunServiceListRequest ) (* pb.WorkflowRunServiceListResponse , error ) {
66123 currentOrg , err := requireCurrentOrg (ctx )
67124 if err != nil {
@@ -185,7 +242,24 @@ func (s *WorkflowRunService) View(ctx context.Context, req *pb.WorkflowRunServic
185242 verificationResult = bizVerificationToPb (vr )
186243 }
187244
188- attestation , err := bizAttestationToPb (run .Attestation )
245+ var predicate chainloop.NormalizablePredicate
246+ if run .Attestation != nil && run .Attestation .Envelope != nil {
247+ predicate , err = chainloop .ExtractPredicate (run .Attestation .Envelope )
248+ if err != nil {
249+ return nil , handleUseCaseErr (err , s .log )
250+ }
251+
252+ if ref := predicate .GetPolicyEvaluationsRef (); ref != nil {
253+ resolved , resolveErr := s .resolvePolicyEvaluations (ctx , ref , run .Workflow .OrgID )
254+ if resolveErr != nil {
255+ s .log .Warnw ("msg" , "failed to resolve policy evaluations from CAS, using inline" , "err" , resolveErr )
256+ } else if resolved != nil {
257+ predicate = & casResolvedPredicate {NormalizablePredicate : predicate , evals : resolved }
258+ }
259+ }
260+ }
261+
262+ attestation , err := bizAttestationToPb (run .Attestation , predicate )
189263 if err != nil {
190264 return nil , handleUseCaseErr (err , s .log )
191265 }
0 commit comments