github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/format/common/spdxhelpers/to_format_model_test.go (about) 1 package spdxhelpers 2 3 import ( 4 "fmt" 5 "regexp" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/google/go-cmp/cmp/cmpopts" 10 "github.com/spdx/tools-golang/spdx" 11 "github.com/spdx/tools-golang/spdx/v2/v2_3" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/anchore/syft/syft/artifact" 16 "github.com/anchore/syft/syft/file" 17 "github.com/anchore/syft/syft/format/internal/spdxutil/helpers" 18 "github.com/anchore/syft/syft/internal/sourcemetadata" 19 "github.com/anchore/syft/syft/pkg" 20 "github.com/anchore/syft/syft/sbom" 21 "github.com/anchore/syft/syft/source" 22 ) 23 24 func Test_toFormatModel(t *testing.T) { 25 tracker := sourcemetadata.NewCompletionTester(t) 26 27 tests := []struct { 28 name string 29 in sbom.SBOM 30 expected *spdx.Document 31 }{ 32 { 33 name: "container", 34 in: sbom.SBOM{ 35 Source: source.Description{ 36 Name: "alpine", 37 Version: "sha256:d34db33f", 38 Metadata: source.ImageMetadata{ 39 UserInput: "alpine:latest", 40 ManifestDigest: "sha256:d34db33f", 41 }, 42 }, 43 Artifacts: sbom.Artifacts{ 44 Packages: pkg.NewCollection(pkg.Package{ 45 Name: "pkg-1", 46 Version: "version-1", 47 }), 48 }, 49 }, 50 expected: &spdx.Document{ 51 SPDXIdentifier: "DOCUMENT", 52 SPDXVersion: spdx.Version, 53 DataLicense: spdx.DataLicense, 54 DocumentName: "alpine", 55 Packages: []*spdx.Package{ 56 { 57 PackageSPDXIdentifier: "Package-pkg-1-pkg-1", 58 PackageName: "pkg-1", 59 PackageVersion: "version-1", 60 PackageSupplier: &spdx.Supplier{ 61 Supplier: "NOASSERTION", 62 }, 63 }, 64 { 65 PackageSPDXIdentifier: "DocumentRoot-Image-alpine", 66 PackageName: "alpine", 67 PackageVersion: "sha256:d34db33f", 68 PrimaryPackagePurpose: "CONTAINER", 69 PackageChecksums: []spdx.Checksum{{Algorithm: "SHA256", Value: "d34db33f"}}, 70 PackageExternalReferences: []*v2_3.PackageExternalReference{ 71 { 72 Category: "PACKAGE-MANAGER", 73 RefType: "purl", 74 Locator: "pkg:oci/alpine@sha256:d34db33f?arch=&tag=latest", 75 }, 76 }, 77 PackageSupplier: &spdx.Supplier{ 78 Supplier: "NOASSERTION", 79 }, 80 }, 81 }, 82 Relationships: []*spdx.Relationship{ 83 { 84 RefA: spdx.DocElementID{ 85 ElementRefID: "DocumentRoot-Image-alpine", 86 }, 87 RefB: spdx.DocElementID{ 88 ElementRefID: "Package-pkg-1-pkg-1", 89 }, 90 Relationship: spdx.RelationshipContains, 91 }, 92 { 93 RefA: spdx.DocElementID{ 94 ElementRefID: "DOCUMENT", 95 }, 96 RefB: spdx.DocElementID{ 97 ElementRefID: "DocumentRoot-Image-alpine", 98 }, 99 Relationship: spdx.RelationshipDescribes, 100 }, 101 }, 102 }, 103 }, 104 { 105 name: "directory", 106 in: sbom.SBOM{ 107 Source: source.Description{ 108 Name: "some/directory", 109 Metadata: source.DirectoryMetadata{ 110 Path: "some/directory", 111 }, 112 }, 113 Artifacts: sbom.Artifacts{ 114 Packages: pkg.NewCollection(pkg.Package{ 115 Name: "pkg-1", 116 Version: "version-1", 117 }), 118 }, 119 }, 120 expected: &spdx.Document{ 121 SPDXIdentifier: "DOCUMENT", 122 SPDXVersion: spdx.Version, 123 DataLicense: spdx.DataLicense, 124 DocumentName: "some/directory", 125 126 Packages: []*spdx.Package{ 127 { 128 PackageSPDXIdentifier: "Package-pkg-1-pkg-1", 129 PackageName: "pkg-1", 130 PackageVersion: "version-1", 131 PackageSupplier: &spdx.Supplier{ 132 Supplier: "NOASSERTION", 133 }, 134 }, 135 { 136 PackageSPDXIdentifier: "DocumentRoot-Directory-some-directory", 137 PackageName: "some/directory", 138 PackageVersion: "", 139 PrimaryPackagePurpose: "FILE", 140 PackageSupplier: &spdx.Supplier{ 141 Supplier: "NOASSERTION", 142 }, 143 }, 144 }, 145 Relationships: []*spdx.Relationship{ 146 { 147 RefA: spdx.DocElementID{ 148 ElementRefID: "DocumentRoot-Directory-some-directory", 149 }, 150 RefB: spdx.DocElementID{ 151 ElementRefID: "Package-pkg-1-pkg-1", 152 }, 153 Relationship: spdx.RelationshipContains, 154 }, 155 { 156 RefA: spdx.DocElementID{ 157 ElementRefID: "DOCUMENT", 158 }, 159 RefB: spdx.DocElementID{ 160 ElementRefID: "DocumentRoot-Directory-some-directory", 161 }, 162 Relationship: spdx.RelationshipDescribes, 163 }, 164 }, 165 }, 166 }, 167 { 168 name: "file", 169 in: sbom.SBOM{ 170 Source: source.Description{ 171 Name: "path/to/some.file", 172 Version: "sha256:d34db33f", 173 Metadata: source.FileMetadata{ 174 Path: "path/to/some.file", 175 Digests: []file.Digest{ 176 { 177 Algorithm: "sha256", 178 Value: "d34db33f", 179 }, 180 }, 181 }, 182 }, 183 Artifacts: sbom.Artifacts{ 184 Packages: pkg.NewCollection(pkg.Package{ 185 Name: "pkg-1", 186 Version: "version-1", 187 }), 188 }, 189 }, 190 expected: &spdx.Document{ 191 SPDXIdentifier: "DOCUMENT", 192 SPDXVersion: spdx.Version, 193 DataLicense: spdx.DataLicense, 194 DocumentName: "path/to/some.file", 195 Packages: []*spdx.Package{ 196 { 197 PackageSPDXIdentifier: "Package-pkg-1-pkg-1", 198 PackageName: "pkg-1", 199 PackageVersion: "version-1", 200 PackageSupplier: &spdx.Supplier{ 201 Supplier: "NOASSERTION", 202 }, 203 }, 204 { 205 PackageSPDXIdentifier: "DocumentRoot-File-path-to-some.file", 206 PackageName: "path/to/some.file", 207 PackageVersion: "sha256:d34db33f", 208 PrimaryPackagePurpose: "FILE", 209 PackageChecksums: []spdx.Checksum{{Algorithm: "SHA256", Value: "d34db33f"}}, 210 PackageSupplier: &spdx.Supplier{ 211 Supplier: "NOASSERTION", 212 }, 213 }, 214 }, 215 Relationships: []*spdx.Relationship{ 216 { 217 RefA: spdx.DocElementID{ 218 ElementRefID: "DocumentRoot-File-path-to-some.file", 219 }, 220 RefB: spdx.DocElementID{ 221 ElementRefID: "Package-pkg-1-pkg-1", 222 }, 223 Relationship: spdx.RelationshipContains, 224 }, 225 { 226 RefA: spdx.DocElementID{ 227 ElementRefID: "DOCUMENT", 228 }, 229 RefB: spdx.DocElementID{ 230 ElementRefID: "DocumentRoot-File-path-to-some.file", 231 }, 232 Relationship: spdx.RelationshipDescribes, 233 }, 234 }, 235 }, 236 }, 237 } 238 239 for _, test := range tests { 240 t.Run(test.name, func(t *testing.T) { 241 tracker.Tested(t, test.in.Source.Metadata) 242 243 // replace IDs with package names 244 var pkgs []pkg.Package 245 for p := range test.in.Artifacts.Packages.Enumerate() { 246 p.OverrideID(artifact.ID(p.Name)) 247 pkgs = append(pkgs, p) 248 } 249 test.in.Artifacts.Packages = pkg.NewCollection(pkgs...) 250 251 // convert 252 got := ToFormatModel(test.in) 253 254 // check differences 255 if diff := cmp.Diff(test.expected, got, 256 cmpopts.IgnoreUnexported(spdx.Document{}, spdx.Package{}), 257 cmpopts.IgnoreFields(spdx.Document{}, "CreationInfo", "DocumentNamespace"), 258 cmpopts.IgnoreFields(spdx.Package{}, "PackageDownloadLocation", "IsFilesAnalyzedTagPresent", "PackageSourceInfo", "PackageLicenseConcluded", "PackageLicenseDeclared", "PackageCopyrightText"), 259 ); diff != "" { 260 t.Error(diff) 261 } 262 }) 263 } 264 } 265 266 func Test_toPackageChecksums(t *testing.T) { 267 tests := []struct { 268 name string 269 pkg pkg.Package 270 expected []spdx.Checksum 271 filesAnalyzed bool 272 }{ 273 { 274 name: "Java Package", 275 pkg: pkg.Package{ 276 Name: "test", 277 Version: "1.0.0", 278 Language: pkg.Java, 279 Metadata: pkg.JavaArchive{ 280 ArchiveDigests: []file.Digest{ 281 { 282 Algorithm: "sha1", // SPDX expects these to be uppercase 283 Value: "1234", 284 }, 285 }, 286 }, 287 }, 288 expected: []spdx.Checksum{ 289 { 290 Algorithm: "SHA1", 291 Value: "1234", 292 }, 293 }, 294 filesAnalyzed: true, 295 }, 296 { 297 name: "Java Package with no archive digests", 298 pkg: pkg.Package{ 299 Name: "test", 300 Version: "1.0.0", 301 Language: pkg.Java, 302 Metadata: pkg.JavaArchive{ 303 ArchiveDigests: []file.Digest{}, 304 }, 305 }, 306 expected: []spdx.Checksum{}, 307 filesAnalyzed: false, 308 }, 309 { 310 name: "Java Package with no metadata", 311 pkg: pkg.Package{ 312 Name: "test", 313 Version: "1.0.0", 314 Language: pkg.Java, 315 }, 316 expected: []spdx.Checksum{}, 317 filesAnalyzed: false, 318 }, 319 { 320 name: "Go Binary Package", 321 pkg: pkg.Package{ 322 Name: "test", 323 Version: "1.0.0", 324 Language: pkg.Go, 325 Metadata: pkg.GolangBinaryBuildinfoEntry{ 326 H1Digest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=", 327 }, 328 }, 329 expected: []spdx.Checksum{ 330 { 331 Algorithm: "SHA256", 332 Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 333 }, 334 }, 335 filesAnalyzed: false, 336 }, 337 { 338 name: "Package with no metadata type", 339 pkg: pkg.Package{ 340 Name: "test", 341 Version: "1.0.0", 342 Language: pkg.Java, 343 Metadata: struct{}{}, 344 }, 345 expected: []spdx.Checksum{}, 346 filesAnalyzed: false, 347 }, 348 } 349 350 for _, test := range tests { 351 t.Run(test.name, func(t *testing.T) { 352 commonSum, filesAnalyzed := toPackageChecksums(test.pkg) 353 assert.ElementsMatch(t, test.expected, commonSum) 354 assert.Equal(t, test.filesAnalyzed, filesAnalyzed) 355 }) 356 } 357 } 358 359 func Test_toFileTypes(t *testing.T) { 360 361 tests := []struct { 362 name string 363 metadata file.Metadata 364 expected []string 365 }{ 366 { 367 name: "application", 368 metadata: file.Metadata{ 369 MIMEType: "application/vnd.unknown", 370 }, 371 expected: []string{ 372 string(helpers.ApplicationFileType), 373 }, 374 }, 375 { 376 name: "archive", 377 metadata: file.Metadata{ 378 MIMEType: "application/zip", 379 }, 380 expected: []string{ 381 string(helpers.ApplicationFileType), 382 string(helpers.ArchiveFileType), 383 }, 384 }, 385 { 386 name: "audio", 387 metadata: file.Metadata{ 388 MIMEType: "audio/ogg", 389 }, 390 expected: []string{ 391 string(helpers.AudioFileType), 392 }, 393 }, 394 { 395 name: "video", 396 metadata: file.Metadata{ 397 MIMEType: "video/3gpp", 398 }, 399 expected: []string{ 400 string(helpers.VideoFileType), 401 }, 402 }, 403 { 404 name: "text", 405 metadata: file.Metadata{ 406 MIMEType: "text/html", 407 }, 408 expected: []string{ 409 string(helpers.TextFileType), 410 }, 411 }, 412 { 413 name: "image", 414 metadata: file.Metadata{ 415 MIMEType: "image/png", 416 }, 417 expected: []string{ 418 string(helpers.ImageFileType), 419 }, 420 }, 421 { 422 name: "binary", 423 metadata: file.Metadata{ 424 MIMEType: "application/x-sharedlib", 425 }, 426 expected: []string{ 427 string(helpers.ApplicationFileType), 428 string(helpers.BinaryFileType), 429 }, 430 }, 431 } 432 for _, test := range tests { 433 t.Run(test.name, func(t *testing.T) { 434 assert.ElementsMatch(t, test.expected, toFileTypes(&test.metadata)) 435 }) 436 } 437 } 438 439 func Test_lookupRelationship(t *testing.T) { 440 441 tests := []struct { 442 input artifact.RelationshipType 443 exists bool 444 ty helpers.RelationshipType 445 comment string 446 }{ 447 { 448 input: artifact.ContainsRelationship, 449 exists: true, 450 ty: helpers.ContainsRelationship, 451 }, 452 { 453 input: artifact.OwnershipByFileOverlapRelationship, 454 exists: true, 455 ty: helpers.OtherRelationship, 456 comment: "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", 457 }, 458 { 459 input: artifact.EvidentByRelationship, 460 exists: true, 461 ty: helpers.OtherRelationship, 462 comment: "evident-by: indicates the package's existence is evident by the given file", 463 }, 464 { 465 input: "made-up", 466 exists: false, 467 }, 468 } 469 for _, test := range tests { 470 t.Run(string(test.input), func(t *testing.T) { 471 exists, ty, comment := lookupRelationship(test.input) 472 assert.Equal(t, exists, test.exists) 473 assert.Equal(t, ty, test.ty) 474 assert.Equal(t, comment, test.comment) 475 }) 476 } 477 } 478 479 func Test_toFileChecksums(t *testing.T) { 480 tests := []struct { 481 name string 482 digests []file.Digest 483 expected []spdx.Checksum 484 }{ 485 { 486 name: "empty", 487 }, 488 { 489 name: "has digests", 490 digests: []file.Digest{ 491 { 492 Algorithm: "SHA256", 493 Value: "deadbeefcafe", 494 }, 495 { 496 Algorithm: "md5", 497 Value: "meh", 498 }, 499 }, 500 expected: []spdx.Checksum{ 501 { 502 Algorithm: "SHA256", 503 Value: "deadbeefcafe", 504 }, 505 { 506 Algorithm: "MD5", 507 Value: "meh", 508 }, 509 }, 510 }, 511 } 512 for _, test := range tests { 513 t.Run(test.name, func(t *testing.T) { 514 assert.ElementsMatch(t, test.expected, toFileChecksums(test.digests)) 515 }) 516 } 517 } 518 519 func Test_fileIDsForPackage(t *testing.T) { 520 p := pkg.Package{ 521 Name: "bogus", 522 } 523 524 c := file.Coordinates{ 525 RealPath: "/path", 526 FileSystemID: "nowhere", 527 } 528 529 docElementId := func(identifiable artifact.Identifiable) spdx.DocElementID { 530 return spdx.DocElementID{ 531 ElementRefID: toSPDXID(identifiable), 532 } 533 } 534 535 tests := []struct { 536 name string 537 relationships []artifact.Relationship 538 expected []*spdx.Relationship 539 }{ 540 { 541 name: "package-to-file contains relationships", 542 relationships: []artifact.Relationship{ 543 { 544 From: p, 545 To: c, 546 Type: artifact.ContainsRelationship, 547 }, 548 }, 549 expected: []*spdx.Relationship{ 550 { 551 Relationship: "CONTAINS", 552 RefA: docElementId(p), 553 RefB: docElementId(c), 554 }, 555 }, 556 }, 557 { 558 name: "package-to-package", 559 relationships: []artifact.Relationship{ 560 { 561 From: p, 562 To: p, 563 Type: artifact.ContainsRelationship, 564 }, 565 }, 566 expected: []*spdx.Relationship{ 567 { 568 Relationship: "CONTAINS", 569 RefA: docElementId(p), 570 RefB: docElementId(p), 571 }, 572 }, 573 }, 574 { 575 name: "ignore file-to-file", 576 relationships: []artifact.Relationship{ 577 { 578 From: c, 579 To: c, 580 Type: artifact.ContainsRelationship, 581 }, 582 }, 583 expected: nil, 584 }, 585 { 586 name: "ignore file-to-package", 587 relationships: []artifact.Relationship{ 588 { 589 From: c, 590 To: p, 591 Type: artifact.ContainsRelationship, 592 }, 593 }, 594 expected: nil, 595 }, 596 { 597 name: "include package-to-file overlap relationships", 598 relationships: []artifact.Relationship{ 599 { 600 From: p, 601 To: c, 602 Type: artifact.OwnershipByFileOverlapRelationship, 603 }, 604 }, 605 expected: []*spdx.Relationship{ 606 { 607 Relationship: "OTHER", 608 RefA: docElementId(p), 609 RefB: docElementId(c), 610 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", 611 }, 612 }, 613 }, 614 } 615 for _, test := range tests { 616 t.Run(test.name, func(t *testing.T) { 617 relationships := toRelationships(test.relationships) 618 assert.Equal(t, test.expected, relationships) 619 }) 620 } 621 } 622 623 func Test_H1Digest(t *testing.T) { 624 s := sbom.SBOM{} 625 tests := []struct { 626 name string 627 pkg pkg.Package 628 expectedDigest string 629 }{ 630 { 631 name: "valid h1digest", 632 pkg: pkg.Package{ 633 Name: "github.com/googleapis/gnostic", 634 Version: "v0.5.5", 635 Metadata: pkg.GolangBinaryBuildinfoEntry{ 636 H1Digest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=", 637 }, 638 }, 639 expectedDigest: "SHA256:f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", 640 }, 641 { 642 name: "invalid h1digest", 643 pkg: pkg.Package{ 644 Name: "github.com/googleapis/gnostic", 645 Version: "v0.5.5", 646 Metadata: pkg.GolangBinaryBuildinfoEntry{ 647 H1Digest: "h1:9fHAtK0uzzz", 648 }, 649 }, 650 expectedDigest: "", 651 }, 652 { 653 name: "unsupported h-digest", 654 pkg: pkg.Package{ 655 Name: "github.com/googleapis/gnostic", 656 Version: "v0.5.5", 657 Metadata: pkg.GolangBinaryBuildinfoEntry{ 658 H1Digest: "h12:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=", 659 }, 660 }, 661 expectedDigest: "", 662 }, 663 } 664 665 for _, test := range tests { 666 t.Run(test.name, func(t *testing.T) { 667 catalog := pkg.NewCollection(test.pkg) 668 pkgs := toPackages(catalog, s) 669 require.Len(t, pkgs, 1) 670 for _, p := range pkgs { 671 if test.expectedDigest == "" { 672 require.Len(t, p.PackageChecksums, 0) 673 } else { 674 require.Len(t, p.PackageChecksums, 1) 675 for _, c := range p.PackageChecksums { 676 require.Equal(t, test.expectedDigest, fmt.Sprintf("%s:%s", c.Algorithm, c.Value)) 677 } 678 } 679 } 680 }) 681 } 682 } 683 684 func Test_OtherLicenses(t *testing.T) { 685 tests := []struct { 686 name string 687 pkg pkg.Package 688 expected []*spdx.OtherLicense 689 }{ 690 { 691 name: "no licenseRef", 692 pkg: pkg.Package{ 693 Licenses: pkg.NewLicenseSet(), 694 }, 695 expected: nil, 696 }, 697 { 698 name: "single licenseRef", 699 pkg: pkg.Package{ 700 Licenses: pkg.NewLicenseSet( 701 pkg.NewLicense("foobar"), 702 ), 703 }, 704 expected: []*spdx.OtherLicense{ 705 { 706 LicenseIdentifier: "LicenseRef-foobar", 707 ExtractedText: "foobar", 708 }, 709 }, 710 }, 711 { 712 name: "multiple licenseRef", 713 pkg: pkg.Package{ 714 Licenses: pkg.NewLicenseSet( 715 pkg.NewLicense("internal made up license name"), 716 pkg.NewLicense("new apple license 2.0"), 717 ), 718 }, 719 expected: []*spdx.OtherLicense{ 720 { 721 LicenseIdentifier: "LicenseRef-internal-made-up-license-name", 722 ExtractedText: "internal made up license name", 723 }, 724 { 725 LicenseIdentifier: "LicenseRef-new-apple-license-2.0", 726 ExtractedText: "new apple license 2.0", 727 }, 728 }, 729 }, 730 } 731 732 for _, test := range tests { 733 t.Run(test.name, func(t *testing.T) { 734 catalog := pkg.NewCollection(test.pkg) 735 otherLicenses := toOtherLicenses(catalog) 736 require.Len(t, otherLicenses, len(test.expected)) 737 require.Equal(t, test.expected, otherLicenses) 738 }) 739 } 740 } 741 742 func Test_toSPDXID(t *testing.T) { 743 tests := []struct { 744 name string 745 it artifact.Identifiable 746 expected string 747 }{ 748 { 749 name: "short filename", 750 it: file.Coordinates{ 751 RealPath: "/short/path/file.txt", 752 }, 753 expected: "File-short-path-file.txt", 754 }, 755 { 756 name: "long filename", 757 it: file.Coordinates{ 758 RealPath: "/some/long/path/with/a/lot/of-text/that-contains-a/file.txt", 759 }, 760 expected: "File-...a-lot-of-text-that-contains-a-file.txt", 761 }, 762 { 763 name: "package", 764 it: pkg.Package{ 765 Type: pkg.NpmPkg, 766 Name: "some-package", 767 }, 768 expected: "Package-npm-some-package", 769 }, 770 } 771 772 for _, test := range tests { 773 t.Run(test.name, func(t *testing.T) { 774 got := string(toSPDXID(test.it)) 775 // trim the hash 776 got = regexp.MustCompile(`-[a-z0-9]*$`).ReplaceAllString(got, "") 777 require.Equal(t, test.expected, got) 778 }) 779 } 780 } 781 782 func Test_otherLicenses(t *testing.T) { 783 pkg1 := pkg.Package{ 784 Name: "first-pkg", 785 Version: "1.1", 786 Licenses: pkg.NewLicenseSet( 787 pkg.NewLicense("MIT"), 788 ), 789 } 790 pkg2 := pkg.Package{ 791 Name: "second-pkg", 792 Version: "2.2", 793 Licenses: pkg.NewLicenseSet( 794 pkg.NewLicense("non spdx license"), 795 ), 796 } 797 bigText := ` 798 Apache License 799 Version 2.0, January 2004` 800 pkg3 := pkg.Package{ 801 Name: "third-pkg", 802 Version: "3.3", 803 Licenses: pkg.NewLicenseSet( 804 pkg.NewLicense(bigText), 805 ), 806 } 807 808 tests := []struct { 809 name string 810 packages []pkg.Package 811 expected []*spdx.OtherLicense 812 }{ 813 { 814 name: "no other licenses when all valid spdx expressions", 815 packages: []pkg.Package{pkg1}, 816 expected: nil, 817 }, 818 { 819 name: "other licenses includes original text", 820 packages: []pkg.Package{pkg2}, 821 expected: []*spdx.OtherLicense{ 822 { 823 LicenseIdentifier: "LicenseRef-non-spdx-license", 824 ExtractedText: "non spdx license", 825 }, 826 }, 827 }, 828 { 829 name: "big licenses get hashed", 830 packages: []pkg.Package{pkg3}, 831 expected: []*spdx.OtherLicense{ 832 { 833 LicenseIdentifier: "LicenseRef-e9a1e42833d3e456f147052f4d312101bd171a0798893169fe596ca6b55c049e", 834 ExtractedText: bigText, 835 }, 836 }, 837 }, 838 } 839 840 for _, test := range tests { 841 t.Run(test.name, func(t *testing.T) { 842 s := sbom.SBOM{ 843 Artifacts: sbom.Artifacts{ 844 Packages: pkg.NewCollection(test.packages...), 845 }, 846 } 847 got := ToFormatModel(s) 848 require.Equal(t, test.expected, got.OtherLicenses) 849 }) 850 } 851 }