github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/formats/common/spdxhelpers/to_gosbom_model_test.go (about) 1 package spdxhelpers 2 3 import ( 4 "testing" 5 6 "github.com/nextlinux/gosbom/gosbom/artifact" 7 "github.com/nextlinux/gosbom/gosbom/file" 8 "github.com/nextlinux/gosbom/gosbom/pkg" 9 "github.com/nextlinux/gosbom/gosbom/source" 10 "github.com/spdx/tools-golang/spdx" 11 "github.com/spdx/tools-golang/spdx/v2/common" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestToGosbomModel(t *testing.T) { 17 sbom, err := ToGosbomModel(&spdx.Document{ 18 SPDXVersion: "1", 19 DataLicense: "GPL", 20 SPDXIdentifier: "id-doc-1", 21 DocumentName: "docName", 22 DocumentNamespace: "docNamespace", 23 ExternalDocumentReferences: nil, 24 DocumentComment: "", 25 CreationInfo: &spdx.CreationInfo{ 26 LicenseListVersion: "", 27 Created: "", 28 CreatorComment: "", 29 }, 30 Packages: []*spdx.Package{ 31 { 32 PackageName: "pkg-1", 33 PackageSPDXIdentifier: "id-pkg-1", 34 PackageVersion: "5.4.3", 35 PackageLicenseDeclared: "", 36 PackageDescription: "", 37 PackageExternalReferences: []*spdx.PackageExternalReference{ 38 { 39 Category: "SECURITY", 40 Locator: "cpe:2.3:a:pkg-1:pkg-1:5.4.3:*:*:*:*:*:*:*", 41 RefType: "cpe23Type", 42 }, 43 { 44 Category: "SECURITY", 45 Locator: "cpe:2.3:a:pkg_1:pkg_1:5.4.3:*:*:*:*:*:*:*", 46 RefType: "cpe23Type", 47 }, 48 { 49 Category: "PACKAGE-MANAGER", 50 Locator: "pkg:apk/alpine/pkg-1@5.4.3?arch=x86_64&upstream=p1-origin&distro=alpine-3.10.9", 51 RefType: "purl", 52 }, 53 }, 54 Files: nil, 55 }, 56 { 57 PackageName: "pkg-2", 58 PackageSPDXIdentifier: "id-pkg-2", 59 PackageVersion: "7.3.1", 60 PackageLicenseDeclared: "", 61 PackageDescription: "", 62 PackageExternalReferences: []*spdx.PackageExternalReference{ 63 { 64 Category: "SECURITY", 65 Locator: "cpe:2.3:a:pkg-2:pkg-2:7.3.1:*:*:*:*:*:*:*", 66 RefType: "cpe23Type", 67 }, 68 { 69 Category: "SECURITY", 70 Locator: "cpe:2.3:a:pkg_2:pkg_2:7.3.1:*:*:*:*:*:*:*", 71 RefType: "cpe23Type", 72 }, 73 { 74 Category: "SECURITY", 75 Locator: "cpe:2.3:a:pkg-2:pkg_2:7.3.1:*:*:*:*:*:*:*", 76 RefType: "cpe23Type", 77 }, 78 { 79 Category: "PACKAGE-MANAGER", 80 Locator: "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=p2-origin@9.1.3&distro=debian-3.10.9", 81 RefType: "purl", 82 }, 83 }, 84 Files: nil, 85 }, 86 }, 87 Relationships: []*spdx.Relationship{}, 88 }) 89 90 assert.NoError(t, err) 91 92 assert.NotNil(t, sbom) 93 94 pkgs := sbom.Artifacts.Packages.Sorted() 95 96 assert.Len(t, pkgs, 2) 97 98 p1 := pkgs[0] 99 assert.Equal(t, p1.Name, "pkg-1") 100 assert.Equal(t, p1.MetadataType, pkg.ApkMetadataType) 101 p1meta := p1.Metadata.(pkg.ApkMetadata) 102 assert.Equal(t, p1meta.OriginPackage, "p1-origin") 103 assert.Len(t, p1.CPEs, 2) 104 105 p2 := pkgs[1] 106 assert.Equal(t, p2.Name, "pkg-2") 107 assert.Equal(t, p2.MetadataType, pkg.DpkgMetadataType) 108 p2meta := p2.Metadata.(pkg.DpkgMetadata) 109 assert.Equal(t, p2meta.Source, "p2-origin") 110 assert.Equal(t, p2meta.SourceVersion, "9.1.3") 111 assert.Len(t, p2.CPEs, 3) 112 } 113 114 func Test_extractMetadata(t *testing.T) { 115 oneTwoThreeFour := 1234 116 tests := []struct { 117 pkg spdx.Package 118 metaType pkg.MetadataType 119 meta interface{} 120 }{ 121 { 122 pkg: spdx.Package{ 123 PackageName: "SomeDebPkg", 124 PackageVersion: "43.1.235", 125 PackageExternalReferences: []*spdx.PackageExternalReference{ 126 { 127 Category: "PACKAGE-MANAGER", 128 Locator: "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=somedebpkg-origin@9.1.3&distro=debian-3.10.9", 129 RefType: "purl", 130 }, 131 }, 132 }, 133 metaType: pkg.DpkgMetadataType, 134 meta: pkg.DpkgMetadata{ 135 Package: "SomeDebPkg", 136 Source: "somedebpkg-origin", 137 Version: "43.1.235", 138 SourceVersion: "9.1.3", 139 Architecture: "x86_64", 140 }, 141 }, 142 { 143 pkg: spdx.Package{ 144 PackageName: "SomeApkPkg", 145 PackageVersion: "3.2.9", 146 PackageExternalReferences: []*spdx.PackageExternalReference{ 147 { 148 Category: "PACKAGE-MANAGER", 149 Locator: "pkg:apk/alpine/pkg-2@7.3.1?arch=x86_64&upstream=apk-origin@9.1.3&distro=alpine-3.10.9", 150 RefType: "purl", 151 }, 152 }, 153 }, 154 metaType: pkg.ApkMetadataType, 155 meta: pkg.ApkMetadata{ 156 Package: "SomeApkPkg", 157 OriginPackage: "apk-origin", 158 Version: "3.2.9", 159 Architecture: "x86_64", 160 }, 161 }, 162 { 163 pkg: spdx.Package{ 164 PackageName: "SomeRpmPkg", 165 PackageVersion: "13.2.79", 166 PackageExternalReferences: []*spdx.PackageExternalReference{ 167 { 168 Category: "PACKAGE-MANAGER", 169 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", 170 RefType: "purl", 171 }, 172 }, 173 }, 174 metaType: pkg.RpmMetadataType, 175 meta: pkg.RpmMetadata{ 176 Name: "SomeRpmPkg", 177 Version: "13.2.79", 178 Epoch: &oneTwoThreeFour, 179 Arch: "x86_64", 180 Release: "", 181 SourceRpm: "some-rpm-origin-1.16.3", 182 }, 183 }, 184 } 185 186 for _, test := range tests { 187 t.Run(test.pkg.PackageName, func(t *testing.T) { 188 info := extractPkgInfo(&test.pkg) 189 metaType, meta := extractMetadata(&test.pkg, info) 190 assert.Equal(t, test.metaType, metaType) 191 assert.EqualValues(t, test.meta, meta) 192 }) 193 } 194 } 195 196 func TestExtractSourceFromNamespaces(t *testing.T) { 197 tests := []struct { 198 namespace string 199 expected source.Scheme 200 }{ 201 { 202 namespace: "https://anchore.com/gosbom/file/d42b01d0-7325-409b-b03f-74082935c4d3", 203 expected: source.FileScheme, 204 }, 205 { 206 namespace: "https://anchore.com/gosbom/image/d42b01d0-7325-409b-b03f-74082935c4d3", 207 expected: source.ImageScheme, 208 }, 209 { 210 namespace: "https://anchore.com/gosbom/dir/d42b01d0-7325-409b-b03f-74082935c4d3", 211 expected: source.DirectoryScheme, 212 }, 213 { 214 namespace: "https://another-host/blob/123", 215 expected: source.UnknownScheme, 216 }, 217 { 218 namespace: "bla bla", 219 expected: source.UnknownScheme, 220 }, 221 { 222 namespace: "", 223 expected: source.UnknownScheme, 224 }, 225 } 226 227 for _, tt := range tests { 228 require.Equal(t, tt.expected, extractSchemeFromNamespace(tt.namespace)) 229 } 230 } 231 232 func TestH1Digest(t *testing.T) { 233 tests := []struct { 234 name string 235 pkg spdx.Package 236 expectedDigest string 237 }{ 238 { 239 name: "valid h1digest", 240 pkg: spdx.Package{ 241 PackageName: "github.com/googleapis/gnostic", 242 PackageVersion: "v0.5.5", 243 PackageExternalReferences: []*spdx.PackageExternalReference{ 244 { 245 Category: "PACKAGE-MANAGER", 246 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 247 RefType: "purl", 248 }, 249 }, 250 PackageChecksums: []spdx.Checksum{ 251 { 252 Algorithm: spdx.SHA256, 253 Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 254 }, 255 }, 256 }, 257 expectedDigest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=", 258 }, 259 { 260 name: "invalid h1digest algorithm", 261 pkg: spdx.Package{ 262 PackageName: "github.com/googleapis/gnostic", 263 PackageVersion: "v0.5.5", 264 PackageExternalReferences: []*spdx.PackageExternalReference{ 265 { 266 Category: "PACKAGE-MANAGER", 267 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 268 RefType: "purl", 269 }, 270 }, 271 PackageChecksums: []spdx.Checksum{ 272 { 273 Algorithm: spdx.SHA1, 274 Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 275 }, 276 }, 277 }, 278 expectedDigest: "", 279 }, 280 { 281 name: "invalid h1digest digest", 282 pkg: spdx.Package{ 283 PackageName: "github.com/googleapis/gnostic", 284 PackageVersion: "v0.5.5", 285 PackageExternalReferences: []*spdx.PackageExternalReference{ 286 { 287 Category: "PACKAGE-MANAGER", 288 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 289 RefType: "purl", 290 }, 291 }, 292 PackageChecksums: []spdx.Checksum{ 293 { 294 Algorithm: spdx.SHA256, 295 Value: "", 296 }, 297 }, 298 }, 299 expectedDigest: "", 300 }, 301 } 302 303 for _, test := range tests { 304 t.Run(test.name, func(t *testing.T) { 305 p := toGosbomPackage(&test.pkg) 306 require.Equal(t, pkg.GolangBinMetadataType, p.MetadataType) 307 meta := p.Metadata.(pkg.GolangBinMetadata) 308 require.Equal(t, test.expectedDigest, meta.H1Digest) 309 }) 310 } 311 } 312 313 func Test_toGosbomRelationships(t *testing.T) { 314 type args struct { 315 spdxIDMap map[string]interface{} 316 doc *spdx.Document 317 } 318 319 pkg1 := pkg.Package{ 320 Name: "github.com/googleapis/gnostic", 321 Version: "v0.5.5", 322 } 323 pkg1.SetID() 324 325 pkg2 := pkg.Package{ 326 Name: "rfc3339", 327 Version: "1.2", 328 Type: pkg.RpmPkg, 329 } 330 pkg2.SetID() 331 332 pkg3 := pkg.Package{ 333 Name: "rfc3339", 334 Version: "1.2", 335 Type: pkg.PythonPkg, 336 } 337 pkg3.SetID() 338 339 loc1 := file.NewLocationFromCoordinates(file.Coordinates{ 340 RealPath: "/somewhere/real", 341 FileSystemID: "abc", 342 }) 343 344 tests := []struct { 345 name string 346 args args 347 want []artifact.Relationship 348 }{ 349 { 350 name: "evident-by relationship", 351 args: args{ 352 spdxIDMap: map[string]interface{}{ 353 string(toSPDXID(pkg1)): &pkg1, 354 string(toSPDXID(loc1)): &loc1, 355 }, 356 doc: &spdx.Document{ 357 Relationships: []*spdx.Relationship{ 358 { 359 RefA: common.DocElementID{ 360 ElementRefID: toSPDXID(pkg1), 361 }, 362 RefB: common.DocElementID{ 363 ElementRefID: toSPDXID(loc1), 364 }, 365 Relationship: spdx.RelationshipOther, 366 RelationshipComment: "evident-by: indicates the package's existence is evident by the given file", 367 }, 368 }, 369 }, 370 }, 371 want: []artifact.Relationship{ 372 { 373 From: pkg1, 374 To: loc1, 375 Type: artifact.EvidentByRelationship, 376 }, 377 }, 378 }, 379 { 380 name: "ownership-by-file-overlap relationship", 381 args: args{ 382 spdxIDMap: map[string]interface{}{ 383 string(toSPDXID(pkg2)): &pkg2, 384 string(toSPDXID(pkg3)): &pkg3, 385 }, 386 doc: &spdx.Document{ 387 Relationships: []*spdx.Relationship{ 388 { 389 RefA: common.DocElementID{ 390 ElementRefID: toSPDXID(pkg2), 391 }, 392 RefB: common.DocElementID{ 393 ElementRefID: toSPDXID(pkg3), 394 }, 395 Relationship: spdx.RelationshipOther, 396 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", 397 }, 398 }, 399 }, 400 }, 401 want: []artifact.Relationship{ 402 { 403 From: pkg2, 404 To: pkg3, 405 Type: artifact.OwnershipByFileOverlapRelationship, 406 }, 407 }, 408 }, 409 } 410 for _, tt := range tests { 411 t.Run(tt.name, func(t *testing.T) { 412 actual := toGosbomRelationships(tt.args.spdxIDMap, tt.args.doc) 413 require.Len(t, actual, len(tt.want)) 414 for i := range actual { 415 require.Equal(t, tt.want[i].From.ID(), actual[i].From.ID()) 416 require.Equal(t, tt.want[i].To.ID(), actual[i].To.ID()) 417 require.Equal(t, tt.want[i].Type, actual[i].Type) 418 } 419 }) 420 } 421 }