@@ -62,6 +62,7 @@ import (
62
62
digest "github.com/opencontainers/go-digest"
63
63
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
64
64
"github.com/pkg/errors"
65
+ spdx "github.com/spdx/tools-golang/spdx/v2_3"
65
66
"github.com/stretchr/testify/require"
66
67
"golang.org/x/crypto/ssh/agent"
67
68
"golang.org/x/sync/errgroup"
@@ -192,6 +193,7 @@ func TestIntegration(t *testing.T) {
192
193
testAttestationBundle ,
193
194
testSBOMScan ,
194
195
testSBOMScanSingleRef ,
196
+ testSBOMSupplements ,
195
197
testMultipleCacheExports ,
196
198
testMountStubsDirectory ,
197
199
testMountStubsTimestamp ,
@@ -8312,6 +8314,154 @@ EOF
8312
8314
require .Subset (t , attest .Predicate , map [string ]interface {}{"name" : "fallback" })
8313
8315
}
8314
8316
8317
+ func testSBOMSupplements (t * testing.T , sb integration.Sandbox ) {
8318
+ integration .CheckFeatureCompat (t , sb , integration .FeatureDirectPush , integration .FeatureSBOM )
8319
+ requiresLinux (t )
8320
+ c , err := New (sb .Context (), sb .Address ())
8321
+ require .NoError (t , err )
8322
+
8323
+ registry , err := sb .NewRegistry ()
8324
+ if errors .Is (err , integration .ErrRequirements ) {
8325
+ t .Skip (err .Error ())
8326
+ }
8327
+
8328
+ p := platforms .MustParse ("linux/amd64" )
8329
+ pk := platforms .Format (p )
8330
+
8331
+ frontend := func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
8332
+ res := gateway .NewResult ()
8333
+
8334
+ // build image
8335
+ st := llb .Scratch ().File (
8336
+ llb .Mkfile ("/foo" , 0600 , []byte {}),
8337
+ )
8338
+ def , err := st .Marshal (ctx )
8339
+ if err != nil {
8340
+ return nil , err
8341
+ }
8342
+ r , err := c .Solve (ctx , gateway.SolveRequest {
8343
+ Definition : def .ToPB (),
8344
+ })
8345
+ if err != nil {
8346
+ return nil , err
8347
+ }
8348
+ ref , err := r .SingleRef ()
8349
+ if err != nil {
8350
+ return nil , err
8351
+ }
8352
+ _ , err = ref .ToState ()
8353
+ if err != nil {
8354
+ return nil , err
8355
+ }
8356
+ res .AddRef (pk , ref )
8357
+
8358
+ expPlatforms := & exptypes.Platforms {
8359
+ Platforms : []exptypes.Platform {{ID : pk , Platform : p }},
8360
+ }
8361
+ dt , err := json .Marshal (expPlatforms )
8362
+ if err != nil {
8363
+ return nil , err
8364
+ }
8365
+ res .AddMeta (exptypes .ExporterPlatformsKey , dt )
8366
+
8367
+ // build attestations
8368
+ doc := spdx.Document {
8369
+ SPDXIdentifier : "DOCUMENT" ,
8370
+ Files : []* spdx.File {
8371
+ {
8372
+ // foo exists...
8373
+ FileSPDXIdentifier : "SPDXRef-File-foo" ,
8374
+ FileName : "/foo" ,
8375
+ },
8376
+ {
8377
+ // ...but bar doesn't
8378
+ FileSPDXIdentifier : "SPDXRef-File-bar" ,
8379
+ FileName : "/bar" ,
8380
+ },
8381
+ },
8382
+ }
8383
+ docBytes , err := json .Marshal (doc )
8384
+ if err != nil {
8385
+ return nil , err
8386
+ }
8387
+ st = llb .Scratch ().
8388
+ File (llb .Mkfile ("/result.spdx" , 0600 , docBytes ))
8389
+ def , err = st .Marshal (ctx )
8390
+ if err != nil {
8391
+ return nil , err
8392
+ }
8393
+ r , err = c .Solve (ctx , gateway.SolveRequest {
8394
+ Definition : def .ToPB (),
8395
+ })
8396
+ if err != nil {
8397
+ return nil , err
8398
+ }
8399
+ refAttest , err := r .SingleRef ()
8400
+ if err != nil {
8401
+ return nil , err
8402
+ }
8403
+ _ , err = ref .ToState ()
8404
+ if err != nil {
8405
+ return nil , err
8406
+ }
8407
+
8408
+ res .AddAttestation (pk , gateway.Attestation {
8409
+ Kind : gatewaypb .AttestationKindInToto ,
8410
+ Ref : refAttest ,
8411
+ Path : "/result.spdx" ,
8412
+ InToto : result.InTotoAttestation {
8413
+ PredicateType : intoto .PredicateSPDX ,
8414
+ },
8415
+ Metadata : map [string ][]byte {
8416
+ result .AttestationSBOMCore : []byte ("result" ),
8417
+ },
8418
+ })
8419
+
8420
+ return res , nil
8421
+ }
8422
+
8423
+ // test the default fallback scanner
8424
+ target := registry + "/buildkit/testsbom:latest"
8425
+ _ , err = c .Build (sb .Context (), SolveOpt {
8426
+ FrontendAttrs : map [string ]string {
8427
+ "attest:sbom" : "" ,
8428
+ },
8429
+ Exports : []ExportEntry {
8430
+ {
8431
+ Type : ExporterImage ,
8432
+ Attrs : map [string ]string {
8433
+ "name" : target ,
8434
+ "push" : "true" ,
8435
+ },
8436
+ },
8437
+ },
8438
+ }, "" , frontend , nil )
8439
+ require .NoError (t , err )
8440
+
8441
+ desc , provider , err := contentutil .ProviderFromRef (target )
8442
+ require .NoError (t , err )
8443
+
8444
+ imgs , err := testutil .ReadImages (sb .Context (), provider , desc )
8445
+ require .NoError (t , err )
8446
+ require .Equal (t , 2 , len (imgs .Images ))
8447
+
8448
+ att := imgs .Find ("unknown/unknown" )
8449
+ attest := struct {
8450
+ intoto.StatementHeader
8451
+ Predicate spdx.Document
8452
+ }{}
8453
+ require .NoError (t , json .Unmarshal (att .LayersRaw [0 ], & attest ))
8454
+ require .Equal (t , "https://in-toto.io/Statement/v0.1" , attest .Type )
8455
+ require .Equal (t , intoto .PredicateSPDX , attest .PredicateType )
8456
+
8457
+ require .Equal (t , "DOCUMENT" , string (attest .Predicate .SPDXIdentifier ))
8458
+ require .Len (t , attest .Predicate .Files , 2 )
8459
+ require .Equal (t , attest .Predicate .Files [0 ].FileName , "/foo" )
8460
+ require .Regexp (t , "^layerID: sha256:" , attest .Predicate .Files [0 ].FileComment )
8461
+ require .Equal (t , attest .Predicate .Files [1 ].FileName , "/bar" )
8462
+ require .Empty (t , attest .Predicate .Files [1 ].FileComment )
8463
+ }
8464
+
8315
8465
func testMultipleCacheExports (t * testing.T , sb integration.Sandbox ) {
8316
8466
integration .CheckFeatureCompat (t , sb , integration .FeatureMultiCacheExport )
8317
8467
c , err := New (sb .Context (), sb .Address ())
0 commit comments