github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/formats/common/spdxhelpers/to_syft_model_test.go (about)

     1  package spdxhelpers
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/google/go-cmp/cmp/cmpopts"
     9  	"github.com/spdx/tools-golang/spdx"
    10  	"github.com/spdx/tools-golang/spdx/v2/common"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/anchore/packageurl-go"
    15  	"github.com/anchore/syft/syft/artifact"
    16  	"github.com/anchore/syft/syft/file"
    17  	"github.com/anchore/syft/syft/pkg"
    18  	"github.com/anchore/syft/syft/sbom"
    19  	"github.com/anchore/syft/syft/source"
    20  )
    21  
    22  func TestToSyftModel(t *testing.T) {
    23  	sbom, err := ToSyftModel(&spdx.Document{
    24  		SPDXVersion:                "1",
    25  		DataLicense:                "GPL",
    26  		SPDXIdentifier:             "id-doc-1",
    27  		DocumentName:               "docName",
    28  		DocumentNamespace:          "docNamespace",
    29  		ExternalDocumentReferences: nil,
    30  		DocumentComment:            "",
    31  		CreationInfo: &spdx.CreationInfo{
    32  			LicenseListVersion: "",
    33  			Created:            "",
    34  			CreatorComment:     "",
    35  		},
    36  		Packages: []*spdx.Package{
    37  			{
    38  				PackageName:            "pkg-1",
    39  				PackageSPDXIdentifier:  "id-pkg-1",
    40  				PackageVersion:         "5.4.3",
    41  				PackageLicenseDeclared: "",
    42  				PackageDescription:     "",
    43  				PackageExternalReferences: []*spdx.PackageExternalReference{
    44  					{
    45  						Category: "SECURITY",
    46  						Locator:  "cpe:2.3:a:pkg-1:pkg-1:5.4.3:*:*:*:*:*:*:*",
    47  						RefType:  "cpe23Type",
    48  					},
    49  					{
    50  						Category: "SECURITY",
    51  						Locator:  "cpe:2.3:a:pkg_1:pkg_1:5.4.3:*:*:*:*:*:*:*",
    52  						RefType:  "cpe23Type",
    53  					},
    54  					{
    55  						Category: "PACKAGE-MANAGER",
    56  						Locator:  "pkg:apk/alpine/pkg-1@5.4.3?arch=x86_64&upstream=p1-origin&distro=alpine-3.10.9",
    57  						RefType:  "purl",
    58  					},
    59  				},
    60  				Files: nil,
    61  			},
    62  			{
    63  				PackageName:            "pkg-2",
    64  				PackageSPDXIdentifier:  "id-pkg-2",
    65  				PackageVersion:         "7.3.1",
    66  				PackageLicenseDeclared: "",
    67  				PackageDescription:     "",
    68  				PackageExternalReferences: []*spdx.PackageExternalReference{
    69  					{
    70  						Category: "SECURITY",
    71  						Locator:  "cpe:2.3:a:pkg-2:pkg-2:7.3.1:*:*:*:*:*:*:*",
    72  						RefType:  "cpe23Type",
    73  					},
    74  					{
    75  						Category: "SECURITY",
    76  						Locator:  "cpe:2.3:a:pkg_2:pkg_2:7.3.1:*:*:*:*:*:*:*",
    77  						RefType:  "cpe23Type",
    78  					},
    79  					{
    80  						Category: "SECURITY",
    81  						Locator:  "cpe:2.3:a:pkg-2:pkg_2:7.3.1:*:*:*:*:*:*:*",
    82  						RefType:  "cpe23Type",
    83  					},
    84  					{
    85  						Category: "PACKAGE-MANAGER",
    86  						Locator:  "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=p2-origin@9.1.3&distro=debian-3.10.9",
    87  						RefType:  "purl",
    88  					},
    89  				},
    90  				Files: nil,
    91  			},
    92  		},
    93  		Relationships: []*spdx.Relationship{},
    94  	})
    95  
    96  	assert.NoError(t, err)
    97  
    98  	assert.NotNil(t, sbom)
    99  
   100  	pkgs := sbom.Artifacts.Packages.Sorted()
   101  
   102  	assert.Len(t, pkgs, 2)
   103  
   104  	p1 := pkgs[0]
   105  	assert.Equal(t, p1.Name, "pkg-1")
   106  	assert.Equal(t, p1.MetadataType, pkg.ApkMetadataType)
   107  	p1meta := p1.Metadata.(pkg.ApkMetadata)
   108  	assert.Equal(t, p1meta.OriginPackage, "p1-origin")
   109  	assert.Len(t, p1.CPEs, 2)
   110  
   111  	p2 := pkgs[1]
   112  	assert.Equal(t, p2.Name, "pkg-2")
   113  	assert.Equal(t, p2.MetadataType, pkg.DpkgMetadataType)
   114  	p2meta := p2.Metadata.(pkg.DpkgMetadata)
   115  	assert.Equal(t, p2meta.Source, "p2-origin")
   116  	assert.Equal(t, p2meta.SourceVersion, "9.1.3")
   117  	assert.Len(t, p2.CPEs, 3)
   118  }
   119  
   120  func Test_extractMetadata(t *testing.T) {
   121  	oneTwoThreeFour := 1234
   122  	tests := []struct {
   123  		pkg      spdx.Package
   124  		metaType pkg.MetadataType
   125  		meta     interface{}
   126  	}{
   127  		{
   128  			pkg: spdx.Package{
   129  				PackageName:    "SomeDebPkg",
   130  				PackageVersion: "43.1.235",
   131  				PackageExternalReferences: []*spdx.PackageExternalReference{
   132  					{
   133  						Category: "PACKAGE-MANAGER",
   134  						Locator:  "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=somedebpkg-origin@9.1.3&distro=debian-3.10.9",
   135  						RefType:  "purl",
   136  					},
   137  				},
   138  			},
   139  			metaType: pkg.DpkgMetadataType,
   140  			meta: pkg.DpkgMetadata{
   141  				Package:       "SomeDebPkg",
   142  				Source:        "somedebpkg-origin",
   143  				Version:       "43.1.235",
   144  				SourceVersion: "9.1.3",
   145  				Architecture:  "x86_64",
   146  			},
   147  		},
   148  		{
   149  			pkg: spdx.Package{
   150  				PackageName:    "SomeApkPkg",
   151  				PackageVersion: "3.2.9",
   152  				PackageExternalReferences: []*spdx.PackageExternalReference{
   153  					{
   154  						Category: "PACKAGE-MANAGER",
   155  						Locator:  "pkg:apk/alpine/pkg-2@7.3.1?arch=x86_64&upstream=apk-origin@9.1.3&distro=alpine-3.10.9",
   156  						RefType:  "purl",
   157  					},
   158  				},
   159  			},
   160  			metaType: pkg.ApkMetadataType,
   161  			meta: pkg.ApkMetadata{
   162  				Package:       "SomeApkPkg",
   163  				OriginPackage: "apk-origin",
   164  				Version:       "3.2.9",
   165  				Architecture:  "x86_64",
   166  			},
   167  		},
   168  		{
   169  			pkg: spdx.Package{
   170  				PackageName:    "SomeRpmPkg",
   171  				PackageVersion: "13.2.79",
   172  				PackageExternalReferences: []*spdx.PackageExternalReference{
   173  					{
   174  						Category: "PACKAGE-MANAGER",
   175  						Locator:  "pkg:rpm/pkg-2@7.3.1?arch=x86_64&epoch=1234&upstream=some-rpm-origin-1.16.3&distro=alpine-3.10.9",
   176  						RefType:  "purl",
   177  					},
   178  				},
   179  			},
   180  			metaType: pkg.RpmMetadataType,
   181  			meta: pkg.RpmMetadata{
   182  				Name:      "SomeRpmPkg",
   183  				Version:   "13.2.79",
   184  				Epoch:     &oneTwoThreeFour,
   185  				Arch:      "x86_64",
   186  				Release:   "",
   187  				SourceRpm: "some-rpm-origin-1.16.3",
   188  			},
   189  		},
   190  	}
   191  
   192  	for _, test := range tests {
   193  		t.Run(test.pkg.PackageName, func(t *testing.T) {
   194  			info := extractPkgInfo(&test.pkg)
   195  			metaType, meta := extractMetadata(&test.pkg, info)
   196  			assert.Equal(t, test.metaType, metaType)
   197  			assert.EqualValues(t, test.meta, meta)
   198  		})
   199  	}
   200  }
   201  
   202  func TestExtractSourceFromNamespaces(t *testing.T) {
   203  	tests := []struct {
   204  		namespace string
   205  		expected  any
   206  	}{
   207  		{
   208  			namespace: "https://anchore.com/syft/file/d42b01d0-7325-409b-b03f-74082935c4d3",
   209  			expected:  source.FileSourceMetadata{},
   210  		},
   211  		{
   212  			namespace: "https://anchore.com/syft/image/d42b01d0-7325-409b-b03f-74082935c4d3",
   213  			expected:  source.StereoscopeImageSourceMetadata{},
   214  		},
   215  		{
   216  			namespace: "https://anchore.com/syft/dir/d42b01d0-7325-409b-b03f-74082935c4d3",
   217  			expected:  source.DirectorySourceMetadata{},
   218  		},
   219  		{
   220  			namespace: "https://another-host/blob/123",
   221  			expected:  nil,
   222  		},
   223  		{
   224  			namespace: "bla bla",
   225  			expected:  nil,
   226  		},
   227  		{
   228  			namespace: "",
   229  			expected:  nil,
   230  		},
   231  	}
   232  
   233  	for _, tt := range tests {
   234  		desc := extractSourceFromNamespace(tt.namespace)
   235  		if tt.expected == nil && desc.Metadata == nil {
   236  			return
   237  		}
   238  		if tt.expected != nil && desc.Metadata == nil {
   239  			t.Fatal("expected metadata but got nil")
   240  		}
   241  		if tt.expected == nil && desc.Metadata != nil {
   242  			t.Fatal("expected nil metadata but got something")
   243  		}
   244  		require.Equal(t, reflect.TypeOf(tt.expected), reflect.TypeOf(desc.Metadata))
   245  	}
   246  }
   247  
   248  func TestH1Digest(t *testing.T) {
   249  	tests := []struct {
   250  		name           string
   251  		pkg            spdx.Package
   252  		expectedDigest string
   253  	}{
   254  		{
   255  			name: "valid h1digest",
   256  			pkg: spdx.Package{
   257  				PackageName:    "github.com/googleapis/gnostic",
   258  				PackageVersion: "v0.5.5",
   259  				PackageExternalReferences: []*spdx.PackageExternalReference{
   260  					{
   261  						Category: "PACKAGE-MANAGER",
   262  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   263  						RefType:  "purl",
   264  					},
   265  				},
   266  				PackageChecksums: []spdx.Checksum{
   267  					{
   268  						Algorithm: spdx.SHA256,
   269  						Value:     "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c",
   270  					},
   271  				},
   272  			},
   273  			expectedDigest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=",
   274  		},
   275  		{
   276  			name: "invalid h1digest algorithm",
   277  			pkg: spdx.Package{
   278  				PackageName:    "github.com/googleapis/gnostic",
   279  				PackageVersion: "v0.5.5",
   280  				PackageExternalReferences: []*spdx.PackageExternalReference{
   281  					{
   282  						Category: "PACKAGE-MANAGER",
   283  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   284  						RefType:  "purl",
   285  					},
   286  				},
   287  				PackageChecksums: []spdx.Checksum{
   288  					{
   289  						Algorithm: spdx.SHA1,
   290  						Value:     "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c",
   291  					},
   292  				},
   293  			},
   294  			expectedDigest: "",
   295  		},
   296  		{
   297  			name: "invalid h1digest digest",
   298  			pkg: spdx.Package{
   299  				PackageName:    "github.com/googleapis/gnostic",
   300  				PackageVersion: "v0.5.5",
   301  				PackageExternalReferences: []*spdx.PackageExternalReference{
   302  					{
   303  						Category: "PACKAGE-MANAGER",
   304  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   305  						RefType:  "purl",
   306  					},
   307  				},
   308  				PackageChecksums: []spdx.Checksum{
   309  					{
   310  						Algorithm: spdx.SHA256,
   311  						Value:     "",
   312  					},
   313  				},
   314  			},
   315  			expectedDigest: "",
   316  		},
   317  	}
   318  
   319  	for _, test := range tests {
   320  		t.Run(test.name, func(t *testing.T) {
   321  			p := toSyftPackage(&test.pkg)
   322  			require.Equal(t, pkg.GolangBinMetadataType, p.MetadataType)
   323  			meta := p.Metadata.(pkg.GolangBinMetadata)
   324  			require.Equal(t, test.expectedDigest, meta.H1Digest)
   325  		})
   326  	}
   327  }
   328  
   329  func Test_toSyftRelationships(t *testing.T) {
   330  	type args struct {
   331  		spdxIDMap map[string]any
   332  		doc       *spdx.Document
   333  	}
   334  
   335  	pkg1 := pkg.Package{
   336  		Name:    "github.com/googleapis/gnostic",
   337  		Version: "v0.5.5",
   338  	}
   339  	pkg1.SetID()
   340  
   341  	pkg2 := pkg.Package{
   342  		Name:    "rfc3339",
   343  		Version: "1.2",
   344  		Type:    pkg.RpmPkg,
   345  	}
   346  	pkg2.SetID()
   347  
   348  	pkg3 := pkg.Package{
   349  		Name:    "rfc3339",
   350  		Version: "1.2",
   351  		Type:    pkg.PythonPkg,
   352  	}
   353  	pkg3.SetID()
   354  
   355  	loc1 := file.NewLocationFromCoordinates(file.Coordinates{
   356  		RealPath:     "/somewhere/real",
   357  		FileSystemID: "abc",
   358  	})
   359  
   360  	tests := []struct {
   361  		name string
   362  		args args
   363  		want []artifact.Relationship
   364  	}{
   365  		{
   366  			name: "evident-by relationship",
   367  			args: args{
   368  				spdxIDMap: map[string]any{
   369  					string(toSPDXID(pkg1)): pkg1,
   370  					string(toSPDXID(loc1)): loc1,
   371  				},
   372  				doc: &spdx.Document{
   373  					Relationships: []*spdx.Relationship{
   374  						{
   375  							RefA: common.DocElementID{
   376  								ElementRefID: toSPDXID(pkg1),
   377  							},
   378  							RefB: common.DocElementID{
   379  								ElementRefID: toSPDXID(loc1),
   380  							},
   381  							Relationship:        spdx.RelationshipOther,
   382  							RelationshipComment: "evident-by: indicates the package's existence is evident by the given file",
   383  						},
   384  					},
   385  				},
   386  			},
   387  			want: []artifact.Relationship{
   388  				{
   389  					From: pkg1,
   390  					To:   loc1,
   391  					Type: artifact.EvidentByRelationship,
   392  				},
   393  			},
   394  		},
   395  		{
   396  			name: "ownership-by-file-overlap relationship",
   397  			args: args{
   398  				spdxIDMap: map[string]any{
   399  					string(toSPDXID(pkg2)): pkg2,
   400  					string(toSPDXID(pkg3)): pkg3,
   401  				},
   402  				doc: &spdx.Document{
   403  					Relationships: []*spdx.Relationship{
   404  						{
   405  							RefA: common.DocElementID{
   406  								ElementRefID: toSPDXID(pkg2),
   407  							},
   408  							RefB: common.DocElementID{
   409  								ElementRefID: toSPDXID(pkg3),
   410  							},
   411  							Relationship:        spdx.RelationshipOther,
   412  							RelationshipComment: "ownership-by-file-overlap: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by",
   413  						},
   414  					},
   415  				},
   416  			},
   417  			want: []artifact.Relationship{
   418  				{
   419  					From: pkg2,
   420  					To:   pkg3,
   421  					Type: artifact.OwnershipByFileOverlapRelationship,
   422  				},
   423  			},
   424  		},
   425  	}
   426  	for _, tt := range tests {
   427  		t.Run(tt.name, func(t *testing.T) {
   428  			actual := toSyftRelationships(tt.args.spdxIDMap, tt.args.doc)
   429  			require.Len(t, actual, len(tt.want))
   430  			for i := range actual {
   431  				require.Equal(t, tt.want[i].From.ID(), actual[i].From.ID())
   432  				require.Equal(t, tt.want[i].To.ID(), actual[i].To.ID())
   433  				require.Equal(t, tt.want[i].Type, actual[i].Type)
   434  			}
   435  		})
   436  	}
   437  }
   438  
   439  func Test_convertToAndFromFormat(t *testing.T) {
   440  	packages := []pkg.Package{
   441  		{
   442  			Name:         "pkg1",
   443  			MetadataType: pkg.UnknownMetadataType,
   444  		},
   445  		{
   446  			Name:         "pkg2",
   447  			MetadataType: pkg.UnknownMetadataType,
   448  		},
   449  	}
   450  
   451  	for i := range packages {
   452  		(&packages[i]).SetID()
   453  	}
   454  
   455  	relationships := []artifact.Relationship{
   456  		{
   457  			From: packages[0],
   458  			To:   packages[1],
   459  			Type: artifact.ContainsRelationship,
   460  		},
   461  	}
   462  
   463  	tests := []struct {
   464  		name          string
   465  		source        source.Description
   466  		packages      []pkg.Package
   467  		relationships []artifact.Relationship
   468  	}{
   469  		{
   470  			name: "image source",
   471  			source: source.Description{
   472  				ID: "DocumentRoot-Image-some-image",
   473  				Metadata: source.StereoscopeImageSourceMetadata{
   474  					ID:             "DocumentRoot-Image-some-image",
   475  					UserInput:      "some-image:some-tag",
   476  					ManifestDigest: "sha256:ab8b83234bc28f28d8e",
   477  				},
   478  				Name:    "some-image",
   479  				Version: "some-tag",
   480  			},
   481  			packages:      packages,
   482  			relationships: relationships,
   483  		},
   484  		{
   485  			name: ". directory source",
   486  			source: source.Description{
   487  				ID:   "DocumentRoot-Directory-.",
   488  				Name: ".",
   489  				Metadata: source.DirectorySourceMetadata{
   490  					Path: ".",
   491  				},
   492  			},
   493  			packages:      packages,
   494  			relationships: relationships,
   495  		},
   496  		{
   497  			name: "directory source",
   498  			source: source.Description{
   499  				ID:   "DocumentRoot-Directory-my-app",
   500  				Name: "my-app",
   501  				Metadata: source.DirectorySourceMetadata{
   502  					Path: "my-app",
   503  				},
   504  			},
   505  			packages:      packages,
   506  			relationships: relationships,
   507  		},
   508  		{
   509  			name: "file source",
   510  			source: source.Description{
   511  				ID: "DocumentRoot-File-my-app.exe",
   512  				Metadata: source.FileSourceMetadata{
   513  					Path: "my-app.exe",
   514  					Digests: []file.Digest{
   515  						{
   516  							Algorithm: "sha256",
   517  							Value:     "3723cae0b8b83234bc28f28d8e",
   518  						},
   519  					},
   520  				},
   521  				Name: "my-app.exe",
   522  			},
   523  			packages:      packages,
   524  			relationships: relationships,
   525  		},
   526  	}
   527  
   528  	for _, test := range tests {
   529  		t.Run(test.name, func(t *testing.T) {
   530  			src := &test.source
   531  			s := sbom.SBOM{
   532  				Source: *src,
   533  				Artifacts: sbom.Artifacts{
   534  					Packages: pkg.NewCollection(test.packages...),
   535  				},
   536  				Relationships: test.relationships,
   537  			}
   538  			doc := ToFormatModel(s)
   539  			got, err := ToSyftModel(doc)
   540  			require.NoError(t, err)
   541  
   542  			if diff := cmp.Diff(&s, got,
   543  				cmpopts.IgnoreUnexported(artifact.Relationship{}),
   544  				cmpopts.IgnoreUnexported(file.LocationSet{}),
   545  				cmpopts.IgnoreUnexported(pkg.Collection{}),
   546  				cmpopts.IgnoreUnexported(pkg.Package{}),
   547  				cmpopts.IgnoreUnexported(pkg.LicenseSet{}),
   548  				cmpopts.IgnoreFields(pkg.Package{}, "MetadataType"),
   549  				cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"),
   550  			); diff != "" {
   551  				t.Fatalf("packages do not match:\n%s", diff)
   552  			}
   553  		})
   554  	}
   555  }
   556  
   557  func Test_purlValue(t *testing.T) {
   558  	tests := []struct {
   559  		purl     packageurl.PackageURL
   560  		expected string
   561  	}{
   562  		{
   563  			purl:     packageurl.PackageURL{},
   564  			expected: "",
   565  		},
   566  		{
   567  			purl: packageurl.PackageURL{
   568  				Name:    "name",
   569  				Version: "version",
   570  			},
   571  			expected: "",
   572  		},
   573  		{
   574  			purl: packageurl.PackageURL{
   575  				Type:    "typ",
   576  				Version: "version",
   577  			},
   578  			expected: "",
   579  		},
   580  		{
   581  			purl: packageurl.PackageURL{
   582  				Type:    "typ",
   583  				Name:    "name",
   584  				Version: "version",
   585  			},
   586  			expected: "pkg:typ/name@version",
   587  		},
   588  		{
   589  			purl: packageurl.PackageURL{
   590  				Type:    "typ",
   591  				Name:    "name",
   592  				Version: "version",
   593  				Qualifiers: packageurl.Qualifiers{
   594  					{
   595  						Key:   "q",
   596  						Value: "v",
   597  					},
   598  				},
   599  			},
   600  			expected: "pkg:typ/name@version?q=v",
   601  		},
   602  	}
   603  
   604  	for _, test := range tests {
   605  		t.Run(test.purl.String(), func(t *testing.T) {
   606  			got := purlValue(test.purl)
   607  			require.Equal(t, test.expected, got)
   608  		})
   609  	}
   610  }
   611  
   612  func Test_directPackageFiles(t *testing.T) {
   613  	doc := &spdx.Document{
   614  		SPDXVersion: "SPDX-2.3",
   615  		Packages: []*spdx.Package{
   616  			{
   617  				PackageName:           "some-package",
   618  				PackageSPDXIdentifier: "1",
   619  				PackageVersion:        "1.0.5",
   620  				Files: []*spdx.File{
   621  					{
   622  						FileName:           "some-file",
   623  						FileSPDXIdentifier: "2",
   624  						Checksums: []spdx.Checksum{
   625  							{
   626  								Algorithm: "SHA1",
   627  								Value:     "a8d733c64f9123",
   628  							},
   629  						},
   630  					},
   631  				},
   632  			},
   633  		},
   634  	}
   635  
   636  	got, err := ToSyftModel(doc)
   637  	require.NoError(t, err)
   638  
   639  	p := pkg.Package{
   640  		Name:         "some-package",
   641  		Version:      "1.0.5",
   642  		MetadataType: pkg.UnknownMetadataType,
   643  	}
   644  	p.SetID()
   645  	f := file.Location{
   646  		LocationData: file.LocationData{
   647  			Coordinates: file.Coordinates{
   648  				RealPath:     "some-file",
   649  				FileSystemID: "",
   650  			},
   651  			VirtualPath: "some-file",
   652  		},
   653  		LocationMetadata: file.LocationMetadata{
   654  			Annotations: map[string]string{},
   655  		},
   656  	}
   657  	s := &sbom.SBOM{
   658  		Artifacts: sbom.Artifacts{
   659  			Packages: pkg.NewCollection(p),
   660  			FileMetadata: map[file.Coordinates]file.Metadata{
   661  				f.Coordinates: {},
   662  			},
   663  			FileDigests: map[file.Coordinates][]file.Digest{
   664  				f.Coordinates: {
   665  					{
   666  						Algorithm: "sha1",
   667  						Value:     "a8d733c64f9123",
   668  					},
   669  				},
   670  			},
   671  		},
   672  		Relationships: []artifact.Relationship{
   673  			{
   674  				From: p,
   675  				To:   f,
   676  				Type: artifact.ContainsRelationship,
   677  			},
   678  		},
   679  		Source:     source.Description{},
   680  		Descriptor: sbom.Descriptor{},
   681  	}
   682  
   683  	require.Equal(t, s, got)
   684  }