@@ -13,6 +13,7 @@ import (
1313 "time"
1414
1515 "github.com/containerd/platforms"
16+ intoto "github.com/in-toto/in-toto-golang/in_toto"
1617 "github.com/moby/buildkit/client/llb"
1718 "github.com/moby/buildkit/client/llb/sourceresolver"
1819 gateway "github.com/moby/buildkit/frontend/gateway/client"
@@ -21,6 +22,8 @@ import (
2122 sourcepolicypb "github.com/moby/buildkit/sourcepolicy/pb"
2223 "github.com/moby/buildkit/sourcepolicy/policysession"
2324 "github.com/moby/buildkit/util/testutil/integration"
25+ "github.com/moby/buildkit/util/testutil/workers"
26+ policyimage "github.com/moby/policy-helpers/image"
2427 digest "github.com/opencontainers/go-digest"
2528 ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
2629 "github.com/pkg/errors"
@@ -270,6 +273,101 @@ func testSourceMetaPolicySession(t *testing.T, sb integration.Sandbox) {
270273 }
271274}
272275
276+ func testSourceMetaPolicySessionResolveAttestations (t * testing.T , sb integration.Sandbox ) {
277+ workers .CheckFeatureCompat (t , sb , workers .FeatureDirectPush , workers .FeatureProvenance )
278+ requiresLinux (t )
279+
280+ ctx := sb .Context ()
281+
282+ c , err := New (ctx , sb .Address ())
283+ require .NoError (t , err )
284+ defer c .Close ()
285+
286+ target , platform := buildProvenanceImage (ctx , t , c , sb )
287+ sourceID := "docker-image://" + target
288+ requestedPredicateType := policyimage .SLSAProvenancePredicateType1
289+
290+ callbackCalls := 0
291+ p := policysession .NewPolicyProvider (func (ctx context.Context , req * policysession.CheckPolicyRequest ) (* policysession.DecisionResponse , * pb.ResolveSourceMetaRequest , error ) {
292+ switch callbackCalls {
293+ case 0 :
294+ callbackCalls ++
295+ require .Equal (t , sourceID , req .Source .Source .Identifier )
296+ require .Nil (t , req .Source .Image )
297+ return nil , & pb.ResolveSourceMetaRequest {
298+ Source : req .Source .Source ,
299+ Platform : req .Platform ,
300+ Image : & pb.ResolveSourceImageRequest {
301+ NoConfig : true ,
302+ ResolveAttestations : []string {requestedPredicateType },
303+ },
304+ }, nil
305+ case 1 :
306+ callbackCalls ++
307+ require .Equal (t , sourceID , req .Source .Source .Identifier )
308+ require .NotNil (t , req .Source .Image )
309+ require .Empty (t , req .Source .Image .Config )
310+ require .NotNil (t , req .Source .Image .AttestationChain )
311+ ac := req .Source .Image .AttestationChain
312+ require .NotEmpty (t , ac .AttestationManifest )
313+
314+ att , ok := ac .Blobs [ac .AttestationManifest ]
315+ require .True (t , ok )
316+ require .NotEmpty (t , att .Data )
317+
318+ var manifest ocispecs.Manifest
319+ require .NoError (t , json .Unmarshal (att .Data , & manifest ))
320+ require .NotEmpty (t , manifest .Layers )
321+
322+ foundRequestedType := false
323+
324+ imageManifestDigest , err := digest .Parse (ac .ImageManifest )
325+ require .NoError (t , err )
326+
327+ for _ , layer := range manifest .Layers {
328+ layerPredicateType := layer .Annotations ["in-toto.io/predicate-type" ]
329+ if layerPredicateType != requestedPredicateType {
330+ continue
331+ }
332+ foundRequestedType = true
333+
334+ blob , ok := ac .Blobs [string (layer .Digest )]
335+ require .True (t , ok , "missing blob for requested predicate type %q" , layerPredicateType )
336+ require .NotEmpty (t , blob .Data , "empty blob data for requested predicate type %q" , layerPredicateType )
337+
338+ var stmt intoto.Statement
339+ require .NoError (t , json .Unmarshal (blob .Data , & stmt ))
340+ require .Equal (t , "https://in-toto.io/Statement/v0.1" , stmt .Type )
341+ require .Equal (t , layerPredicateType , stmt .PredicateType )
342+ require .NotEmpty (t , stmt .Subject )
343+ require .Equal (t , imageManifestDigest .Hex (), stmt .Subject [0 ].Digest ["sha256" ])
344+ }
345+ require .True (t , foundRequestedType , "requested predicate type %q not found in attestation manifest layers" , requestedPredicateType )
346+
347+ return & policysession.DecisionResponse {
348+ Action : sourcepolicypb .PolicyAction_ALLOW ,
349+ }, nil , nil
350+ default :
351+ return nil , nil , errors .Errorf ("too many policy callbacks: %d" , callbackCalls )
352+ }
353+ })
354+
355+ _ , err = c .Build (ctx , SolveOpt {
356+ SourcePolicyProvider : p ,
357+ }, "test" , func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
358+ _ , err := c .ResolveSourceMetadata (ctx , & opspb.SourceOp {
359+ Identifier : sourceID ,
360+ }, sourceresolver.Opt {
361+ ImageOpt : & sourceresolver.ResolveImageOpt {
362+ Platform : & platform ,
363+ },
364+ })
365+ return nil , err
366+ }, nil )
367+ require .NoError (t , err )
368+ require .Equal (t , 2 , callbackCalls )
369+ }
370+
273371func testSourcePolicyParallelSession (t * testing.T , sb integration.Sandbox ) {
274372 requiresLinux (t )
275373
0 commit comments