@@ -17,7 +17,6 @@ package service
1717
1818import (
1919 "context"
20- "encoding/base64"
2120 "encoding/json"
2221 "fmt"
2322 "sort"
@@ -30,13 +29,11 @@ import (
3029 "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware"
3130 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz"
3231 casJWT "github.com/chainloop-dev/chainloop/internal/robotaccount/cas"
32+ "github.com/chainloop-dev/chainloop/pkg/attestation"
3333 "github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop"
3434 "github.com/chainloop-dev/chainloop/pkg/credentials"
35- protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1"
36- "google.golang.org/protobuf/encoding/protojson"
37-
3835 errors "github.com/go-kratos/kratos/v2/errors"
39- "github.com/secure-systems-lab /go-securesystemslib/dsse "
36+ v1 "github.com/google /go-containerregistry/pkg/v1 "
4037)
4138
4239type AttestationService struct {
@@ -189,90 +186,79 @@ func (s *AttestationService) Init(ctx context.Context, req *cpAPI.AttestationSer
189186}
190187
191188func (s * AttestationService ) Store (ctx context.Context , req * cpAPI.AttestationServiceStoreRequest ) (* cpAPI.AttestationServiceStoreResponse , error ) {
192- var envelope dsse.Envelope
193189 robotAccount := usercontext .CurrentRobotAccount (ctx )
194190 if robotAccount == nil {
195191 return nil , errors .NotFound ("not found" , "robot account not found" )
196192 }
197193
198- // Try unmarshalling a bundle first, falling back to plain dsse envelopes
199- var bundle protobundle.Bundle
200- // nolint: gocritic
201- if req .GetBundle () != nil {
202- if err := protojson .Unmarshal (req .GetBundle (), & bundle ); err != nil {
203- return nil , handleUseCaseErr (err , s .log )
204- }
205- envelope = * envelopeFromBundle (& bundle )
206- } else if req .GetAttestation () != nil {
207- // trying with envelope instead
208- if err := json .Unmarshal (req .GetAttestation (), & envelope ); err != nil {
209- return nil , handleUseCaseErr (err , s .log )
210- }
211- } else {
212- return nil , errors .BadRequest ("attestation" , "DSSE envelope or attestation bundle is required" )
194+ if req .GetAttestation () == nil && req .GetBundle () == nil {
195+ return nil , errors .BadRequest ("input required" , "DSSE envelope or attestation bundle is required" )
213196 }
214197
215- digest , err := s .storeAttestation (ctx , & envelope , & bundle , robotAccount , req .WorkflowRunId , req .MarkVersionAsReleased )
198+ // This will make sure the provided workflowRunID belongs to the org encoded in the robot account
199+ wf , err := s .findWorkflowFromTokenOrNameOrRunID (ctx , robotAccount .OrgID , "" , "" , req .WorkflowRunId )
216200 if err != nil {
217201 return nil , handleUseCaseErr (err , s .log )
218202 }
219203
220- return & cpAPI.AttestationServiceStoreResponse {
221- Result : & cpAPI.AttestationServiceStoreResponse_Result {Digest : digest },
222- }, nil
223- }
204+ wRun , err := s .wrUseCase .GetByIDInOrgOrPublic (ctx , robotAccount .OrgID , req .WorkflowRunId )
205+ if err != nil {
206+ return nil , handleUseCaseErr (err , s .log )
207+ } else if wRun == nil {
208+ return nil , errors .NotFound ("not found" , "workflow run not found" )
209+ }
224210
225- // Extracts a DSSE envelope from a Sigstore bundle (Sigstore bundles have their own protobuf implementation for DSSE)
226- func envelopeFromBundle (bundle * protobundle.Bundle ) * dsse.Envelope {
227- sigstoreEnvelope := bundle .GetDsseEnvelope ()
228- return & dsse.Envelope {
229- PayloadType : sigstoreEnvelope .PayloadType ,
230- Payload : base64 .StdEncoding .EncodeToString (sigstoreEnvelope .Payload ),
231- Signatures : []dsse.Signature {
232- {
233- KeyID : sigstoreEnvelope .GetSignatures ()[0 ].GetKeyid (),
234- Sig : string (sigstoreEnvelope .GetSignatures ()[0 ].GetSig ()),
235- },
236- },
211+ if len (wRun .CASBackends ) == 0 {
212+ return nil , errors .NotFound ("not found" , "workflow run has no CAS backend" )
237213 }
238- }
239214
240- // Stores and process a DSSE Envelope with a Chainloop attestation
241- func (s * AttestationService ) storeAttestation (ctx context.Context , envelope * dsse.Envelope , bundle * protobundle.Bundle , robotAccount * usercontext.RobotAccount , workflowRunID string , markAsReleased * bool ) (string , error ) {
242- // This will make sure the provided workflowRunID belongs to the org encoded in the robot account
243- wf , err := s .findWorkflowFromTokenOrNameOrRunID (ctx , robotAccount .OrgID , "" , "" , workflowRunID )
215+ digest , err := s .storeAttestation (ctx , req .GetAttestation (), req .GetBundle (), robotAccount , wf , wRun , req .MarkVersionAsReleased )
244216 if err != nil {
245- return "" , handleUseCaseErr (err , s .log )
217+ return nil , handleUseCaseErr (err , s .log )
246218 }
247219
248- wRun , err := s .wrUseCase .GetByIDInOrgOrPublic (ctx , robotAccount .OrgID , workflowRunID )
220+ return & cpAPI.AttestationServiceStoreResponse {
221+ Result : & cpAPI.AttestationServiceStoreResponse_Result {Digest : digest .String ()},
222+ }, nil
223+ }
224+
225+ // Stores and process a DSSE Envelope with a Chainloop attestation
226+ func (s * AttestationService ) storeAttestation (ctx context.Context , envelope []byte , bundle []byte , robotAccount * usercontext.RobotAccount , wf * biz.Workflow , wfRun * biz.WorkflowRun , markAsReleased * bool ) (* v1.Hash , error ) {
227+ workflowRunID := wfRun .ID .String ()
228+ casBackend := wfRun .CASBackends [0 ]
229+
230+ // extract structured envelope for integrations
231+ dsseEnv , err := attestation .DSSEEnvelopeFromRaw (bundle , envelope )
249232 if err != nil {
250- return "" , handleUseCaseErr (err , s .log )
251- } else if wRun == nil {
252- return "" , errors .NotFound ("not found" , "workflow run not found" )
233+ return nil , handleUseCaseErr (err , s .log )
253234 }
254235
255- if len (wRun .CASBackends ) == 0 {
256- return "" , errors .NotFound ("not found" , "workflow run has no CAS backend" )
236+ // Store the attestation
237+ digest , err := s .wrUseCase .SaveAttestation (ctx , workflowRunID , envelope , bundle )
238+ if err != nil {
239+ return nil , handleUseCaseErr (err , s .log )
257240 }
258241
259- // We currently only support one backend per workflowRun
260- casBackend := wRun .CASBackends [0 ]
261-
262242 // If we have an external CAS backend, we will push there the attestation
263243 if ! casBackend .Inline {
264244 go func () {
265245 b := backoff .NewExponentialBackOff ()
266246 b .MaxElapsedTime = 1 * time .Minute
267247 err := backoff .Retry (
268248 func () error {
249+ rawContent := bundle
250+ if rawContent == nil {
251+ rawContent = envelope
252+ }
253+
269254 // reset context
270255 ctx := context .Background ()
271- digest , err := s . attestationUseCase . UploadToCAS ( ctx , envelope , casBackend , workflowRunID )
272- if err != nil {
256+ var err error
257+ if err = s . attestationUseCase . UploadAttestationToCAS ( ctx , rawContent , casBackend , workflowRunID , * digest ); err != nil {
273258 return err
274259 }
275- s .log .Infow ("msg" , "attestation uploaded to CAS" , "digest" , digest .String (), "runID" , workflowRunID )
260+
261+ s .log .Infow ("msg" , "attestation uploaded to CAS" , "digest" , digest , "runID" , workflowRunID )
276262 return nil
277263 }, b )
278264
@@ -282,28 +268,22 @@ func (s *AttestationService) storeAttestation(ctx context.Context, envelope *dss
282268 }()
283269 }
284270
285- // Store the attestation including the digest in the CAS backend (if exists)
286- digest , err := s .wrUseCase .SaveAttestation (ctx , workflowRunID , envelope , bundle )
287- if err != nil {
288- return "" , handleUseCaseErr (err , s .log )
289- }
290-
291271 // Store the exploded attestation referrer information in the DB
292- if err := s .referrerUseCase .ExtractAndPersist (ctx , envelope , wf .ID .String ()); err != nil {
293- return "" , handleUseCaseErr (err , s .log )
272+ if err := s .referrerUseCase .ExtractAndPersist (ctx , dsseEnv , * digest , wf .ID .String ()); err != nil {
273+ return nil , handleUseCaseErr (err , s .log )
294274 }
295275
296276 if ! casBackend .Inline {
297277 // Store the mappings in the DB
298- references , err := s .casMappingUseCase .LookupDigestsInAttestation (envelope )
278+ references , err := s .casMappingUseCase .LookupDigestsInAttestation (dsseEnv , * digest )
299279 if err != nil {
300- return "" , handleUseCaseErr (err , s .log )
280+ return nil , handleUseCaseErr (err , s .log )
301281 }
302282
303283 for _ , ref := range references {
304284 s .log .Infow ("msg" , "creating CAS mapping" , "name" , ref .Name , "digest" , ref .Digest , "workflowRun" , workflowRunID , "casBackend" , casBackend .ID .String ())
305285 if _ , err := s .casMappingUseCase .Create (ctx , ref .Digest , casBackend .ID .String (), workflowRunID ); err != nil {
306- return "" , handleUseCaseErr (err , s .log )
286+ return nil , handleUseCaseErr (err , s .log )
307287 }
308288 }
309289 }
@@ -313,7 +293,7 @@ func (s *AttestationService) storeAttestation(ctx context.Context, envelope *dss
313293 // Run integrations dispatcher
314294 go func () {
315295 if err := s .integrationDispatcher .Run (context .TODO (), & dispatcher.RunOpts {
316- Envelope : envelope , OrgID : robotAccount .OrgID , WorkflowID : wf . ID . String () ,
296+ Envelope : dsseEnv , OrgID : robotAccount .OrgID , WorkflowID : workflowRunID ,
317297 DownloadBackendType : string (casBackend .Provider ),
318298 DownloadSecretName : secretName ,
319299 WorkflowRunID : workflowRunID ,
@@ -325,17 +305,17 @@ func (s *AttestationService) storeAttestation(ctx context.Context, envelope *dss
325305 // promote release if the workflowRun is successful
326306 if markAsReleased != nil && * markAsReleased {
327307 // Update the project version to mark it as a release
328- if _ , err := s .projectVersionUseCase .UpdateReleaseStatus (ctx , wRun .ProjectVersion .ID .String (), true ); err != nil {
329- return "" , handleUseCaseErr (err , s .log )
308+ if _ , err := s .projectVersionUseCase .UpdateReleaseStatus (ctx , wfRun .ProjectVersion .ID .String (), true ); err != nil {
309+ return nil , handleUseCaseErr (err , s .log )
330310 }
331311 }
332312
333313 if err := s .wrUseCase .MarkAsFinished (ctx , workflowRunID , biz .WorkflowRunSuccess , "" ); err != nil {
334- return "" , handleUseCaseErr (err , s .log )
314+ return nil , handleUseCaseErr (err , s .log )
335315 }
336316
337317 // Record the attestation in the prometheus registry
338- _ = s .prometheusUseCase .ObserveAttestationIfNeeded (ctx , wRun , biz .WorkflowRunSuccess )
318+ _ = s .prometheusUseCase .ObserveAttestationIfNeeded (ctx , wfRun , biz .WorkflowRunSuccess )
339319
340320 return digest , nil
341321}
@@ -487,6 +467,7 @@ func bizAttestationToPb(att *biz.Attestation) (*cpAPI.AttestationItem, error) {
487467 Blocked : policyEvaluationStatus .Blocked ,
488468 HasViolations : policyEvaluationStatus .HasViolations ,
489469 },
470+ Bundle : att .Bundle ,
490471 }, nil
491472}
492473
0 commit comments