@@ -250,6 +250,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
250250 testHTTPResolveMetaReuse ,
251251 testHTTPResolveMultiBuild ,
252252 testGitResolveMutatedSource ,
253+ testImageResolveAttestationChainRequiresNetwork ,
253254}
254255
255256func TestIntegration (t * testing.T ) {
@@ -12249,6 +12250,92 @@ func testHTTPResolveSourceMetadata(t *testing.T, sb integration.Sandbox) {
1224912250 require .NoError (t , err )
1225012251}
1225112252
12253+ func testImageResolveAttestationChainRequiresNetwork (t * testing.T , sb integration.Sandbox ) {
12254+ // this test temporarily requires direct registry access as the integration test
12255+ // mirroring system does not support mirroring attestation chains yet.
12256+ // Support is coming in future buildx release.
12257+ ctx := sb .Context ()
12258+ c , err := New (ctx , sb .Address ())
12259+ require .NoError (t , err )
12260+ defer c .Close ()
12261+
12262+ amd64 , err := platforms .Parse ("linux/amd64" )
12263+ require .NoError (t , err )
12264+
12265+ _ , err = c .Build (ctx , SolveOpt {}, "test" , func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
12266+ const rootDigest = "sha256:4e91099af134f4b1f509fecbd1a55981dfff18d8029d6782e28413210ef468b7"
12267+ const imageDigest = "sha256:d2f0b39234a66c3d58f24200d3fda9e4e3d2263c09f9b7286a826ab639713047"
12268+ const attestationDigest = "sha256:fb4c46b14f52d1bf790f593921c52dddc698fc6780792fb8469fc60efc0e609b"
12269+ const sigDigest = "sha256:bd0a6b088440ba9838e8eec79e736128fe52afce934578eae30ca8675f6d3142"
12270+
12271+ id := "registry-1-stage.docker.io/docker/github-builder-test@" + rootDigest
12272+ md , err := c .ResolveSourceMetadata (ctx , & pb.SourceOp {
12273+ Identifier : "docker-image://" + id ,
12274+ }, sourceresolver.Opt {
12275+ ImageOpt : & sourceresolver.ResolveImageOpt {
12276+ NoConfig : true ,
12277+ AttestationChain : true ,
12278+ Platform : & amd64 ,
12279+ },
12280+ })
12281+ if err != nil {
12282+ return nil , err
12283+ }
12284+ require .Equal (t , rootDigest , md .Image .Digest .String ())
12285+ require .Nil (t , md .Image .Config )
12286+ require .NotNil (t , md .Image )
12287+ require .NotNil (t , md .Image .AttestationChain )
12288+ ac := md .Image .AttestationChain
12289+ require .Equal (t , rootDigest , ac .Root .String ())
12290+ require .Equal (t , imageDigest , ac .ImageManifest .String ())
12291+ require .Equal (t , attestationDigest , ac .AttestationManifest .String ())
12292+ require .Len (t , ac .SignatureManifests , 1 )
12293+ require .Equal (t , sigDigest , ac .SignatureManifests [0 ].String ())
12294+
12295+ desc := ac .Blobs [ac .Root ]
12296+ require .Equal (t , rootDigest , desc .Descriptor .Digest .String ())
12297+ require .Len (t , desc .Data , int (desc .Descriptor .Size ))
12298+ require .Equal (t , ocispecs .MediaTypeImageIndex , desc .Descriptor .MediaType )
12299+ chk := digest .FromBytes (desc .Data )
12300+ require .Equal (t , rootDigest , chk .String ())
12301+
12302+ desc = ac .Blobs [ac .ImageManifest ]
12303+ // image manifest is expected to be missing as content is not needed to verify attestation chain
12304+ _ , ok := ac .Blobs [digest .Digest (imageDigest )]
12305+ require .False (t , ok )
12306+
12307+ desc = ac .Blobs [ac .AttestationManifest ]
12308+ require .Equal (t , attestationDigest , desc .Descriptor .Digest .String ())
12309+ require .Equal (t , ocispecs .MediaTypeImageManifest , desc .Descriptor .MediaType )
12310+ require .Len (t , desc .Data , int (desc .Descriptor .Size ))
12311+ chk = digest .FromBytes (desc .Data )
12312+ require .Equal (t , attestationDigest , chk .String ())
12313+
12314+ desc = ac .Blobs [ac .SignatureManifests [0 ]]
12315+ require .Equal (t , sigDigest , desc .Descriptor .Digest .String ())
12316+ require .Equal (t , ocispecs .MediaTypeImageManifest , desc .Descriptor .MediaType )
12317+ require .Len (t , desc .Data , int (desc .Descriptor .Size ))
12318+ chk = digest .FromBytes (desc .Data )
12319+ require .Equal (t , sigDigest , chk .String ())
12320+
12321+ var sigMfst ocispecs.Manifest
12322+ err = json .Unmarshal (ac .Blobs [ac .SignatureManifests [0 ]].Data , & sigMfst )
12323+ require .NoError (t , err )
12324+ require .Equal (t , 1 , len (sigMfst .Layers ))
12325+ sigLayer := sigMfst .Layers [0 ]
12326+ sigDesc := ac .Blobs [digest .Digest (sigLayer .Digest )]
12327+ require .Equal (t , sigLayer .Digest , sigDesc .Descriptor .Digest )
12328+ require .Len (t , sigDesc .Data , int (sigDesc .Descriptor .Size ))
12329+ require .Equal (t , "application/vnd.dev.sigstore.bundle.v0.3+json" , sigDesc .Descriptor .MediaType )
12330+ chk = digest .FromBytes (sigDesc .Data )
12331+ require .Equal (t , sigLayer .Digest , chk )
12332+
12333+ require .Len (t , md .Image .AttestationChain .Blobs , 4 )
12334+ return nil , nil
12335+ }, nil )
12336+ require .NoError (t , err )
12337+ }
12338+
1225212339func testHTTPPruneAfterCacheKey (t * testing.T , sb integration.Sandbox ) {
1225312340 // this test depends on hitting race condition in internal functions.
1225412341 // If debugging and expecting failure you can add small sleep in beginning of source/http.Exec() to hit reliably
0 commit comments