@@ -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"
@@ -189,6 +190,7 @@ func TestIntegration(t *testing.T) {
189
190
testAttestationBundle ,
190
191
testSBOMScan ,
191
192
testSBOMScanSingleRef ,
193
+ testSBOMSupplements ,
192
194
testMultipleCacheExports ,
193
195
testMountStubsDirectory ,
194
196
testMountStubsTimestamp ,
@@ -8199,6 +8201,154 @@ EOF
8199
8201
require .Subset (t , attest .Predicate , map [string ]interface {}{"name" : "fallback" })
8200
8202
}
8201
8203
8204
+ func testSBOMSupplements (t * testing.T , sb integration.Sandbox ) {
8205
+ integration .CheckFeatureCompat (t , sb , integration .FeatureDirectPush , integration .FeatureSBOM )
8206
+ requiresLinux (t )
8207
+ c , err := New (sb .Context (), sb .Address ())
8208
+ require .NoError (t , err )
8209
+
8210
+ registry , err := sb .NewRegistry ()
8211
+ if errors .Is (err , integration .ErrRequirements ) {
8212
+ t .Skip (err .Error ())
8213
+ }
8214
+
8215
+ p := platforms .MustParse ("linux/amd64" )
8216
+ pk := platforms .Format (p )
8217
+
8218
+ frontend := func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
8219
+ res := gateway .NewResult ()
8220
+
8221
+ // build image
8222
+ st := llb .Scratch ().File (
8223
+ llb .Mkfile ("/foo" , 0600 , []byte {}),
8224
+ )
8225
+ def , err := st .Marshal (ctx )
8226
+ if err != nil {
8227
+ return nil , err
8228
+ }
8229
+ r , err := c .Solve (ctx , gateway.SolveRequest {
8230
+ Definition : def .ToPB (),
8231
+ })
8232
+ if err != nil {
8233
+ return nil , err
8234
+ }
8235
+ ref , err := r .SingleRef ()
8236
+ if err != nil {
8237
+ return nil , err
8238
+ }
8239
+ _ , err = ref .ToState ()
8240
+ if err != nil {
8241
+ return nil , err
8242
+ }
8243
+ res .AddRef (pk , ref )
8244
+
8245
+ expPlatforms := & exptypes.Platforms {
8246
+ Platforms : []exptypes.Platform {{ID : pk , Platform : p }},
8247
+ }
8248
+ dt , err := json .Marshal (expPlatforms )
8249
+ if err != nil {
8250
+ return nil , err
8251
+ }
8252
+ res .AddMeta (exptypes .ExporterPlatformsKey , dt )
8253
+
8254
+ // build attestations
8255
+ doc := spdx.Document {
8256
+ SPDXIdentifier : "DOCUMENT" ,
8257
+ Files : []* spdx.File {
8258
+ {
8259
+ // foo exists...
8260
+ FileSPDXIdentifier : "SPDXRef-File-foo" ,
8261
+ FileName : "/foo" ,
8262
+ },
8263
+ {
8264
+ // ...but bar doesn't
8265
+ FileSPDXIdentifier : "SPDXRef-File-bar" ,
8266
+ FileName : "/bar" ,
8267
+ },
8268
+ },
8269
+ }
8270
+ docBytes , err := json .Marshal (doc )
8271
+ if err != nil {
8272
+ return nil , err
8273
+ }
8274
+ st = llb .Scratch ().
8275
+ File (llb .Mkfile ("/result.spdx" , 0600 , docBytes ))
8276
+ def , err = st .Marshal (ctx )
8277
+ if err != nil {
8278
+ return nil , err
8279
+ }
8280
+ r , err = c .Solve (ctx , gateway.SolveRequest {
8281
+ Definition : def .ToPB (),
8282
+ })
8283
+ if err != nil {
8284
+ return nil , err
8285
+ }
8286
+ refAttest , err := r .SingleRef ()
8287
+ if err != nil {
8288
+ return nil , err
8289
+ }
8290
+ _ , err = ref .ToState ()
8291
+ if err != nil {
8292
+ return nil , err
8293
+ }
8294
+
8295
+ res .AddAttestation (pk , gateway.Attestation {
8296
+ Kind : gatewaypb .AttestationKindInToto ,
8297
+ Ref : refAttest ,
8298
+ Path : "/result.spdx" ,
8299
+ InToto : result.InTotoAttestation {
8300
+ PredicateType : intoto .PredicateSPDX ,
8301
+ },
8302
+ Metadata : map [string ][]byte {
8303
+ result .AttestationSBOMCore : []byte ("result" ),
8304
+ },
8305
+ })
8306
+
8307
+ return res , nil
8308
+ }
8309
+
8310
+ // test the default fallback scanner
8311
+ target := registry + "/buildkit/testsbom:latest"
8312
+ _ , err = c .Build (sb .Context (), SolveOpt {
8313
+ FrontendAttrs : map [string ]string {
8314
+ "attest:sbom" : "" ,
8315
+ },
8316
+ Exports : []ExportEntry {
8317
+ {
8318
+ Type : ExporterImage ,
8319
+ Attrs : map [string ]string {
8320
+ "name" : target ,
8321
+ "push" : "true" ,
8322
+ },
8323
+ },
8324
+ },
8325
+ }, "" , frontend , nil )
8326
+ require .NoError (t , err )
8327
+
8328
+ desc , provider , err := contentutil .ProviderFromRef (target )
8329
+ require .NoError (t , err )
8330
+
8331
+ imgs , err := testutil .ReadImages (sb .Context (), provider , desc )
8332
+ require .NoError (t , err )
8333
+ require .Equal (t , 2 , len (imgs .Images ))
8334
+
8335
+ att := imgs .Find ("unknown/unknown" )
8336
+ attest := struct {
8337
+ intoto.StatementHeader
8338
+ Predicate spdx.Document
8339
+ }{}
8340
+ require .NoError (t , json .Unmarshal (att .LayersRaw [0 ], & attest ))
8341
+ require .Equal (t , "https://in-toto.io/Statement/v0.1" , attest .Type )
8342
+ require .Equal (t , intoto .PredicateSPDX , attest .PredicateType )
8343
+
8344
+ require .Equal (t , "DOCUMENT" , string (attest .Predicate .SPDXIdentifier ))
8345
+ require .Len (t , attest .Predicate .Files , 2 )
8346
+ require .Equal (t , attest .Predicate .Files [0 ].FileName , "/foo" )
8347
+ require .Regexp (t , "^layerID: sha256:" , attest .Predicate .Files [0 ].FileComment )
8348
+ require .Equal (t , attest .Predicate .Files [1 ].FileName , "/bar" )
8349
+ require .Empty (t , attest .Predicate .Files [1 ].FileComment )
8350
+ }
8351
+
8202
8352
func testMultipleCacheExports (t * testing.T , sb integration.Sandbox ) {
8203
8353
integration .CheckFeatureCompat (t , sb , integration .FeatureMultiCacheExport )
8204
8354
c , err := New (sb .Context (), sb .Address ())
0 commit comments