github.com/anchore/syft@v1.38.2/syft/format/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 p1meta := p1.Metadata.(pkg.ApkDBEntry) 107 assert.Equal(t, p1meta.OriginPackage, "p1-origin") 108 assert.Len(t, p1.CPEs, 2) 109 110 p2 := pkgs[1] 111 assert.Equal(t, p2.Name, "pkg-2") 112 p2meta := p2.Metadata.(pkg.DpkgDBEntry) 113 assert.Equal(t, p2meta.Source, "p2-origin") 114 assert.Equal(t, p2meta.SourceVersion, "9.1.3") 115 assert.Len(t, p2.CPEs, 3) 116 } 117 118 func Test_extractMetadata(t *testing.T) { 119 oneTwoThreeFour := 1234 120 tests := []struct { 121 pkg spdx.Package 122 meta interface{} 123 }{ 124 { 125 pkg: spdx.Package{ 126 PackageName: "SomeDebPkg", 127 PackageVersion: "43.1.235", 128 PackageExternalReferences: []*spdx.PackageExternalReference{ 129 { 130 Category: "PACKAGE-MANAGER", 131 Locator: "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=somedebpkg-origin@9.1.3&distro=debian-3.10.9", 132 RefType: "purl", 133 }, 134 }, 135 }, 136 meta: pkg.DpkgDBEntry{ 137 Package: "SomeDebPkg", 138 Source: "somedebpkg-origin", 139 Version: "43.1.235", 140 SourceVersion: "9.1.3", 141 Architecture: "x86_64", 142 }, 143 }, 144 { 145 pkg: spdx.Package{ 146 PackageName: "SomeApkPkg", 147 PackageVersion: "3.2.9", 148 PackageExternalReferences: []*spdx.PackageExternalReference{ 149 { 150 Category: "PACKAGE-MANAGER", 151 Locator: "pkg:apk/alpine/pkg-2@7.3.1?arch=x86_64&upstream=apk-origin@9.1.3&distro=alpine-3.10.9", 152 RefType: "purl", 153 }, 154 }, 155 }, 156 meta: pkg.ApkDBEntry{ 157 Package: "SomeApkPkg", 158 OriginPackage: "apk-origin", 159 Version: "3.2.9", 160 Architecture: "x86_64", 161 }, 162 }, 163 { 164 pkg: spdx.Package{ 165 PackageName: "SomeRpmPkg", 166 PackageVersion: "13.2.79", 167 PackageExternalReferences: []*spdx.PackageExternalReference{ 168 { 169 Category: "PACKAGE-MANAGER", 170 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", 171 RefType: "purl", 172 }, 173 }, 174 }, 175 meta: pkg.RpmDBEntry{ 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 meta := extractMetadata(&test.pkg, info) 190 assert.EqualValues(t, test.meta, meta) 191 }) 192 } 193 } 194 195 func TestExtractSourceFromNamespaces(t *testing.T) { 196 tests := []struct { 197 namespace string 198 expected any 199 }{ 200 { 201 namespace: "https://anchore.com/syft/file/d42b01d0-7325-409b-b03f-74082935c4d3", 202 expected: source.FileMetadata{}, 203 }, 204 { 205 namespace: "https://anchore.com/syft/image/d42b01d0-7325-409b-b03f-74082935c4d3", 206 expected: source.ImageMetadata{}, 207 }, 208 { 209 namespace: "https://anchore.com/syft/dir/d42b01d0-7325-409b-b03f-74082935c4d3", 210 expected: source.DirectoryMetadata{}, 211 }, 212 { 213 namespace: "https://another-host/blob/123", 214 expected: nil, 215 }, 216 { 217 namespace: "bla bla", 218 expected: nil, 219 }, 220 { 221 namespace: "", 222 expected: nil, 223 }, 224 } 225 226 for _, tt := range tests { 227 desc := extractSourceFromNamespace(tt.namespace) 228 if tt.expected == nil && desc.Metadata == nil { 229 return 230 } 231 if tt.expected != nil && desc.Metadata == nil { 232 t.Fatal("expected metadata but got nil") 233 } 234 if tt.expected == nil && desc.Metadata != nil { 235 t.Fatal("expected nil metadata but got something") 236 } 237 require.Equal(t, reflect.TypeOf(tt.expected), reflect.TypeOf(desc.Metadata)) 238 } 239 } 240 241 func TestH1Digest(t *testing.T) { 242 tests := []struct { 243 name string 244 pkg spdx.Package 245 expectedDigest string 246 }{ 247 { 248 name: "valid h1digest", 249 pkg: spdx.Package{ 250 PackageName: "github.com/googleapis/gnostic", 251 PackageVersion: "v0.5.5", 252 PackageExternalReferences: []*spdx.PackageExternalReference{ 253 { 254 Category: "PACKAGE-MANAGER", 255 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 256 RefType: "purl", 257 }, 258 }, 259 PackageChecksums: []spdx.Checksum{ 260 { 261 Algorithm: spdx.SHA256, 262 Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 263 }, 264 }, 265 }, 266 expectedDigest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=", 267 }, 268 { 269 name: "invalid h1digest algorithm", 270 pkg: spdx.Package{ 271 PackageName: "github.com/googleapis/gnostic", 272 PackageVersion: "v0.5.5", 273 PackageExternalReferences: []*spdx.PackageExternalReference{ 274 { 275 Category: "PACKAGE-MANAGER", 276 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 277 RefType: "purl", 278 }, 279 }, 280 PackageChecksums: []spdx.Checksum{ 281 { 282 Algorithm: spdx.SHA1, 283 Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 284 }, 285 }, 286 }, 287 expectedDigest: "", 288 }, 289 { 290 name: "invalid h1digest digest", 291 pkg: spdx.Package{ 292 PackageName: "github.com/googleapis/gnostic", 293 PackageVersion: "v0.5.5", 294 PackageExternalReferences: []*spdx.PackageExternalReference{ 295 { 296 Category: "PACKAGE-MANAGER", 297 Locator: "pkg:golang/github.com/googleapis/gnostic@v0.5.5", 298 RefType: "purl", 299 }, 300 }, 301 PackageChecksums: []spdx.Checksum{ 302 { 303 Algorithm: spdx.SHA256, 304 Value: "", 305 }, 306 }, 307 }, 308 expectedDigest: "", 309 }, 310 } 311 312 for _, test := range tests { 313 t.Run(test.name, func(t *testing.T) { 314 p := toSyftPackage(&test.pkg) 315 meta := p.Metadata.(pkg.GolangBinaryBuildinfoEntry) 316 require.Equal(t, test.expectedDigest, meta.H1Digest) 317 }) 318 } 319 } 320 321 func Test_toSyftRelationships(t *testing.T) { 322 type args struct { 323 spdxIDMap map[string]any 324 doc *spdx.Document 325 } 326 327 pkg1 := pkg.Package{ 328 Name: "github.com/googleapis/gnostic", 329 Version: "v0.5.5", 330 } 331 pkg1.SetID() 332 333 pkg2 := pkg.Package{ 334 Name: "rfc3339", 335 Version: "1.2", 336 Type: pkg.RpmPkg, 337 } 338 pkg2.SetID() 339 340 pkg3 := pkg.Package{ 341 Name: "rfc3339", 342 Version: "1.2", 343 Type: pkg.PythonPkg, 344 } 345 pkg3.SetID() 346 347 loc1 := file.NewLocationFromCoordinates(file.Coordinates{ 348 RealPath: "/somewhere/real", 349 FileSystemID: "abc", 350 }) 351 352 tests := []struct { 353 name string 354 args args 355 want []artifact.Relationship 356 }{ 357 { 358 name: "evident-by relationship", 359 args: args{ 360 spdxIDMap: map[string]any{ 361 string(toSPDXID(pkg1)): pkg1, 362 string(toSPDXID(loc1)): loc1, 363 }, 364 doc: &spdx.Document{ 365 Relationships: []*spdx.Relationship{ 366 { 367 RefA: common.DocElementID{ 368 ElementRefID: toSPDXID(pkg1), 369 }, 370 RefB: common.DocElementID{ 371 ElementRefID: toSPDXID(loc1), 372 }, 373 Relationship: spdx.RelationshipOther, 374 RelationshipComment: "evident-by: indicates the package's existence is evident by the given file", 375 }, 376 }, 377 }, 378 }, 379 want: []artifact.Relationship{ 380 { 381 From: pkg1, 382 To: loc1, 383 Type: artifact.EvidentByRelationship, 384 }, 385 }, 386 }, 387 { 388 name: "ownership-by-file-overlap relationship", 389 args: args{ 390 spdxIDMap: map[string]any{ 391 string(toSPDXID(pkg2)): pkg2, 392 string(toSPDXID(pkg3)): pkg3, 393 }, 394 doc: &spdx.Document{ 395 Relationships: []*spdx.Relationship{ 396 { 397 RefA: common.DocElementID{ 398 ElementRefID: toSPDXID(pkg2), 399 }, 400 RefB: common.DocElementID{ 401 ElementRefID: toSPDXID(pkg3), 402 }, 403 Relationship: spdx.RelationshipOther, 404 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", 405 }, 406 }, 407 }, 408 }, 409 want: []artifact.Relationship{ 410 { 411 From: pkg2, 412 To: pkg3, 413 Type: artifact.OwnershipByFileOverlapRelationship, 414 }, 415 }, 416 }, 417 { 418 name: "dependency-of relationship", 419 args: args{ 420 spdxIDMap: map[string]any{ 421 string(toSPDXID(pkg2)): pkg2, 422 string(toSPDXID(pkg3)): pkg3, 423 }, 424 doc: &spdx.Document{ 425 Relationships: []*spdx.Relationship{ 426 { 427 RefA: common.DocElementID{ 428 ElementRefID: toSPDXID(pkg2), 429 }, 430 RefB: common.DocElementID{ 431 ElementRefID: toSPDXID(pkg3), 432 }, 433 Relationship: spdx.RelationshipDependencyOf, 434 RelationshipComment: "dependency-of: indicates that the package in RefA is a dependency of the package in RefB", 435 }, 436 }, 437 }, 438 }, 439 want: []artifact.Relationship{ 440 { 441 From: pkg2, 442 To: pkg3, 443 Type: artifact.DependencyOfRelationship, 444 }, 445 }, 446 }, 447 { 448 name: "dependends-on relationship", 449 args: args{ 450 spdxIDMap: map[string]any{ 451 string(toSPDXID(pkg2)): pkg2, 452 string(toSPDXID(pkg3)): pkg3, 453 }, 454 doc: &spdx.Document{ 455 Relationships: []*spdx.Relationship{ 456 { 457 RefA: common.DocElementID{ 458 ElementRefID: toSPDXID(pkg3), 459 }, 460 RefB: common.DocElementID{ 461 ElementRefID: toSPDXID(pkg2), 462 }, 463 Relationship: spdx.RelationshipDependsOn, 464 RelationshipComment: "dependends-on: indicates that the package in RefA depends on the package in RefB", 465 }, 466 }, 467 }, 468 }, 469 want: []artifact.Relationship{ 470 { 471 From: pkg2, 472 To: pkg3, 473 Type: artifact.DependencyOfRelationship, 474 }, 475 }, 476 }, 477 } 478 for _, tt := range tests { 479 t.Run(tt.name, func(t *testing.T) { 480 actual := toSyftRelationships(tt.args.spdxIDMap, tt.args.doc) 481 require.Len(t, actual, len(tt.want)) 482 for i := range actual { 483 require.Equal(t, tt.want[i].From.ID(), actual[i].From.ID()) 484 require.Equal(t, tt.want[i].To.ID(), actual[i].To.ID()) 485 require.Equal(t, tt.want[i].Type, actual[i].Type) 486 } 487 }) 488 } 489 } 490 491 func Test_convertToAndFromFormat(t *testing.T) { 492 packages := []pkg.Package{ 493 { 494 Name: "pkg1", 495 }, 496 { 497 Name: "pkg2", 498 }, 499 } 500 501 for i := range packages { 502 (&packages[i]).SetID() 503 } 504 505 relationships := []artifact.Relationship{ 506 { 507 From: packages[0], 508 To: packages[1], 509 Type: artifact.ContainsRelationship, 510 }, 511 } 512 513 tests := []struct { 514 name string 515 source source.Description 516 packages []pkg.Package 517 relationships []artifact.Relationship 518 }{ 519 { 520 name: "image source", 521 source: source.Description{ 522 ID: "DocumentRoot-Image-some-image", 523 Metadata: source.ImageMetadata{ 524 ID: "DocumentRoot-Image-some-image", 525 UserInput: "some-image:some-tag", 526 ManifestDigest: "sha256:ab8b83234bc28f28d8e", 527 }, 528 Name: "some-image", 529 Version: "some-tag", 530 Supplier: "some-supplier", 531 }, 532 packages: packages, 533 relationships: relationships, 534 }, 535 { 536 name: ". directory source with supplier", 537 source: source.Description{ 538 ID: "DocumentRoot-Directory-.", 539 Name: ".", 540 Supplier: "some-supplier", 541 Metadata: source.DirectoryMetadata{ 542 Path: ".", 543 }, 544 }, 545 packages: packages, 546 relationships: relationships, 547 }, 548 { 549 name: "directory source without supplier", 550 source: source.Description{ 551 ID: "DocumentRoot-Directory-my-app", 552 Name: "my-app", 553 Metadata: source.DirectoryMetadata{ 554 Path: "my-app", 555 }, 556 }, 557 packages: packages, 558 relationships: relationships, 559 }, 560 { 561 name: "file source", 562 source: source.Description{ 563 ID: "DocumentRoot-File-my-app.exe", 564 Metadata: source.FileMetadata{ 565 Path: "my-app.exe", 566 Digests: []file.Digest{ 567 { 568 Algorithm: "sha256", 569 Value: "3723cae0b8b83234bc28f28d8e", 570 }, 571 }, 572 }, 573 Name: "my-app.exe", 574 }, 575 packages: packages, 576 relationships: relationships, 577 }, 578 } 579 580 for _, test := range tests { 581 t.Run(test.name, func(t *testing.T) { 582 src := &test.source 583 s := sbom.SBOM{ 584 Source: *src, 585 Artifacts: sbom.Artifacts{ 586 Packages: pkg.NewCollection(test.packages...), 587 }, 588 Relationships: test.relationships, 589 } 590 doc := ToFormatModel(s) 591 got, err := ToSyftModel(doc) 592 require.NoError(t, err) 593 594 if diff := cmp.Diff(&s, got, 595 cmpopts.IgnoreUnexported(artifact.Relationship{}), 596 cmpopts.IgnoreUnexported(file.LocationSet{}), 597 cmpopts.IgnoreUnexported(pkg.Collection{}), 598 cmpopts.IgnoreUnexported(pkg.Package{}), 599 cmpopts.IgnoreUnexported(pkg.LicenseSet{}), 600 cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"), 601 ); diff != "" { 602 t.Fatalf("packages do not match:\n%s", diff) 603 } 604 }) 605 } 606 } 607 608 func Test_purlValue(t *testing.T) { 609 tests := []struct { 610 purl packageurl.PackageURL 611 expected string 612 }{ 613 { 614 purl: packageurl.PackageURL{}, 615 expected: "", 616 }, 617 { 618 purl: packageurl.PackageURL{ 619 Name: "name", 620 Version: "version", 621 }, 622 expected: "", 623 }, 624 { 625 purl: packageurl.PackageURL{ 626 Type: "typ", 627 Version: "version", 628 }, 629 expected: "", 630 }, 631 { 632 purl: packageurl.PackageURL{ 633 Type: "typ", 634 Name: "name", 635 Version: "version", 636 }, 637 expected: "pkg:typ/name@version", 638 }, 639 { 640 purl: packageurl.PackageURL{ 641 Type: "typ", 642 Name: "name", 643 Version: "version", 644 Qualifiers: packageurl.Qualifiers{ 645 { 646 Key: "q", 647 Value: "v", 648 }, 649 }, 650 }, 651 expected: "pkg:typ/name@version?q=v", 652 }, 653 } 654 655 for _, test := range tests { 656 t.Run(test.purl.String(), func(t *testing.T) { 657 got := purlValue(test.purl) 658 require.Equal(t, test.expected, got) 659 }) 660 } 661 } 662 663 func Test_directPackageFiles(t *testing.T) { 664 doc := &spdx.Document{ 665 SPDXVersion: "SPDX-2.3", 666 Packages: []*spdx.Package{ 667 { 668 PackageName: "some-package", 669 PackageSPDXIdentifier: "1", // important! 670 PackageVersion: "1.0.5", 671 Files: []*spdx.File{ 672 { 673 FileName: "some-file", 674 FileSPDXIdentifier: "2", 675 Checksums: []spdx.Checksum{ 676 { 677 Algorithm: "SHA1", 678 Value: "a8d733c64f9123", 679 }, 680 }, 681 }, 682 }, 683 }, 684 }, 685 } 686 687 got, err := ToSyftModel(doc) 688 require.NoError(t, err) 689 690 p := pkg.Package{ 691 Name: "some-package", 692 Version: "1.0.5", 693 } 694 p.OverrideID("1") // the same as the spdxID on the package element 695 f := file.Location{ 696 LocationData: file.LocationData{ 697 Coordinates: file.Coordinates{ 698 RealPath: "some-file", 699 FileSystemID: "", 700 }, 701 AccessPath: "some-file", 702 }, 703 LocationMetadata: file.LocationMetadata{ 704 Annotations: map[string]string{}, 705 }, 706 } 707 s := &sbom.SBOM{ 708 Artifacts: sbom.Artifacts{ 709 Packages: pkg.NewCollection(p), 710 FileMetadata: map[file.Coordinates]file.Metadata{ 711 f.Coordinates: {}, 712 }, 713 FileDigests: map[file.Coordinates][]file.Digest{ 714 f.Coordinates: { 715 { 716 Algorithm: "sha1", 717 Value: "a8d733c64f9123", 718 }, 719 }, 720 }, 721 }, 722 Relationships: []artifact.Relationship{ 723 { 724 From: p, 725 To: f, 726 Type: artifact.ContainsRelationship, 727 }, 728 }, 729 Source: source.Description{}, 730 Descriptor: sbom.Descriptor{}, 731 } 732 733 require.Equal(t, s, got) 734 } 735 736 func Test_useSPDXIdentifierOverDerivedSyftArtifactID(t *testing.T) { 737 doc := &spdx.Document{ 738 SPDXVersion: "SPDX-2.3", 739 Packages: []*spdx.Package{ 740 { 741 PackageName: "some-package", 742 PackageSPDXIdentifier: "1", // important! 743 PackageVersion: "1.0.5", 744 Files: []*spdx.File{ 745 { 746 FileName: "some-file", 747 FileSPDXIdentifier: "2", 748 Checksums: []spdx.Checksum{ 749 { 750 Algorithm: "SHA1", 751 Value: "a8d733c64f9123", 752 }, 753 }, 754 }, 755 }, 756 }, 757 }, 758 } 759 s, err := ToSyftModel(doc) 760 761 assert.Nil(t, err) 762 assert.NotNil(t, s.Artifacts.Packages.Package("1")) 763 } 764 765 func Test_skipsPackagesWithGeneratedFromRelationship(t *testing.T) { 766 doc := &spdx.Document{ 767 SPDXVersion: "SPDX-2.3", 768 Packages: []*spdx.Package{ 769 { 770 PackageName: "package-1", 771 PackageSPDXIdentifier: "1", 772 PackageVersion: "1.0.5", 773 }, 774 { 775 PackageName: "package-1-src", 776 PackageSPDXIdentifier: "1-src", 777 PackageVersion: "1.0.5-src", 778 }, 779 }, 780 Relationships: []*spdx.Relationship{ 781 { 782 Relationship: spdx.RelationshipGeneratedFrom, 783 RefA: common.DocElementID{ // package 1 784 ElementRefID: spdx.ElementID("1"), 785 }, 786 RefB: common.DocElementID{ // generated from package 1-src 787 ElementRefID: spdx.ElementID("1-src"), 788 }, 789 }, 790 }, 791 } 792 s, err := ToSyftModel(doc) 793 794 assert.Nil(t, err) 795 assert.NotNil(t, s.Artifacts.Packages.Package("1")) 796 assert.Nil(t, s.Artifacts.Packages.Package("1-src")) 797 }