// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later package parser2v3 import ( "fmt" "testing" "github.com/spdx/tools-golang/spdx/common" "github.com/spdx/tools-golang/spdx/v2_3" ) // ===== Parser package section state change tests ===== func TestParser2_3PackageStartsNewPackageAfterParsingPackageNameTag(t *testing.T) { // create the first package pkgOldName := "p1" parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: pkgOldName, PackageSPDXIdentifier: "p1"}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair2_3("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } // state should be correct if parser.st != psPackage2_3 { t.Errorf("expected state to be %v, got %v", psPackage2_3, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and it should not be pkgOld if parser.pkg == pkgOld { t.Errorf("expected new package, got pkgOld") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should still be of size 1 and have pkgOld only if parser.doc.Packages[0] != pkgOld { t.Errorf("Expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } } func TestParser2_3PackageStartsNewPackageAfterParsingPackageNameTagWhileInUnpackaged(t *testing.T) { // pkg is nil, so that Files appearing before the first PackageName tag // are added to Files instead of Packages parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psFile2_3, pkg: nil, } // the Document's Packages should be empty if len(parser.doc.Packages) != 0 { t.Errorf("Expected zero packages, got %d", len(parser.doc.Packages)) } // now add a new package pkgName := "p2" err := parser.parsePair2_3("PackageName", pkgName) if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } // state should be correct if parser.st != psPackage2_3 { t.Errorf("expected state to be %v, got %v", psPackage2_3, parser.st) } // and a package should be created if parser.pkg == nil { t.Fatalf("parser didn't create new package") } // and the package name should be as expected if parser.pkg.PackageName != pkgName { t.Errorf("expected package name %s, got %s", pkgName, parser.pkg.PackageName) } // and the package should default to true for FilesAnalyzed if parser.pkg.FilesAnalyzed != true { t.Errorf("expected FilesAnalyzed to default to true, got false") } if parser.pkg.IsFilesAnalyzedTagPresent != false { t.Errorf("expected IsFilesAnalyzedTagPresent to default to false, got true") } // and the Document's Packages should be of size 0, because the prior was // unpackaged files and this one won't be added until an SPDXID is seen if len(parser.doc.Packages) != 0 { t.Errorf("Expected %v packages in doc, got %v", 0, len(parser.doc.Packages)) } } func TestParser2_3PackageMovesToFileAfterParsingFileNameTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) pkgCurrent := parser.pkg err := parser.parsePair2_3("FileName", "testFile") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } // state should be correct if parser.st != psFile2_3 { t.Errorf("expected state to be %v, got %v", psFile2_3, parser.st) } // and current package should remain what it was if parser.pkg != pkgCurrent { t.Fatalf("expected package to remain %v, got %v", pkgCurrent, parser.pkg) } } func TestParser2_3PackageMovesToOtherLicenseAfterParsingLicenseIDTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("LicenseID", "LicenseRef-TestLic") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psOtherLicense2_3 { t.Errorf("expected state to be %v, got %v", psOtherLicense2_3, parser.st) } } func TestParser2_3PackageMovesToReviewAfterParsingReviewerTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("Reviewer", "Person: John Doe") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psReview2_3 { t.Errorf("expected state to be %v, got %v", psReview2_3, parser.st) } } func TestParser2_3PackageStaysAfterParsingRelationshipTags(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-else") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } // state should remain unchanged if parser.st != psPackage2_3 { t.Errorf("expected state to be %v, got %v", psPackage2_3, parser.st) } err = parser.parsePair2_3("RelationshipComment", "blah") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } // state should still remain unchanged if parser.st != psPackage2_3 { t.Errorf("expected state to be %v, got %v", psPackage2_3, parser.st) } } func TestParser2_3PackageStaysAfterParsingAnnotationTags(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psPackage2_3 { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage2_3) } err = parser.parsePair2_3("AnnotationDate", "2018-09-15T00:36:00Z") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psPackage2_3 { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage2_3) } err = parser.parsePair2_3("AnnotationType", "REVIEW") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psPackage2_3 { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage2_3) } err = parser.parsePair2_3("SPDXREF", "SPDXRef-45") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psPackage2_3 { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage2_3) } err = parser.parsePair2_3("AnnotationComment", "i guess i had something to say about this package") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.st != psPackage2_3 { t.Errorf("parser is in state %v, expected %v", parser.st, psPackage2_3) } } // ===== Package data section tests ===== func TestParser2_3CanParsePackageTags(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageName != "p1" { t.Errorf("got %v for PackageName", parser.pkg.PackageName) } // still should not yet be in Packages map, b/c no SPDX identifier if len(parser.doc.Packages) != 0 { t.Errorf("expected 0 packages, got %d", len(parser.doc.Packages)) } // Package SPDX Identifier err = parser.parsePairFromPackage2_3("SPDXID", "SPDXRef-p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // "SPDXRef-" prefix should be removed from the item if parser.pkg.PackageSPDXIdentifier != "p1" { t.Errorf("got %v for PackageSPDXIdentifier", parser.pkg.PackageSPDXIdentifier) } // and it should now be added to the Packages map if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } if parser.doc.Packages[0] != parser.pkg { t.Errorf("expected to point to parser.pkg, got %v", parser.doc.Packages[0]) } // Package Version err = parser.parsePairFromPackage2_3("PackageVersion", "2.1.1") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVersion != "2.1.1" { t.Errorf("got %v for PackageVersion", parser.pkg.PackageVersion) } // Package File Name err = parser.parsePairFromPackage2_3("PackageFileName", "p1-2.1.1.tar.gz") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageFileName != "p1-2.1.1.tar.gz" { t.Errorf("got %v for PackageFileName", parser.pkg.PackageFileName) } // Package Supplier // SKIP -- separate tests for subvalues below // Package Originator // SKIP -- separate tests for subvalues below // Package Download Location err = parser.parsePairFromPackage2_3("PackageDownloadLocation", "https://example.com/whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDownloadLocation != "https://example.com/whatever" { t.Errorf("got %v for PackageDownloadLocation", parser.pkg.PackageDownloadLocation) } // Files Analyzed err = parser.parsePairFromPackage2_3("FilesAnalyzed", "false") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != false { t.Errorf("got %v for FilesAnalyzed", parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("got %v for IsFilesAnalyzedTagPresent", parser.pkg.IsFilesAnalyzedTagPresent) } // Package Verification Code // SKIP -- separate tests for "excludes", or not, below testChecksums := map[common.ChecksumAlgorithm]string{ "MD5": "624c1abb3664f4b35547e7c73864ad24", "SHA1": "85ed0817af83a24ad8da68c2b5094de69833983c", "SHA256": "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd", "SHA512": "4ced3267f5ed38df65ceebc43e97aa6c2948cc7ef3288c2e5074e7df7fab544cc93339604513ea5f65616f9ed1c48581465043c8a9b693ef20fd4fddaf25e1b9", } for algo, tc := range testChecksums { if err := parser.parsePairFromPackage2_3( "PackageChecksum", fmt.Sprintf("%s: %s", algo, tc)); err != nil { t.Errorf("expected error, got %v", err) } } for _, checksum := range parser.pkg.PackageChecksums { if checksum.Value != testChecksums[checksum.Algorithm] { t.Errorf( "expected %s for PackageChecksum%s, got %s", testChecksums[checksum.Algorithm], checksum.Algorithm, checksum.Value, ) } } // Package Home Page err = parser.parsePairFromPackage2_3("PackageHomePage", "https://example.com/whatever2") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageHomePage != "https://example.com/whatever2" { t.Errorf("got %v for PackageHomePage", parser.pkg.PackageHomePage) } // Package Source Info err = parser.parsePairFromPackage2_3("PackageSourceInfo", "random comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSourceInfo != "random comment" { t.Errorf("got %v for PackageSourceInfo", parser.pkg.PackageSourceInfo) } // Package License Concluded err = parser.parsePairFromPackage2_3("PackageLicenseConcluded", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseConcluded != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseConcluded", parser.pkg.PackageLicenseConcluded) } // All Licenses Info From Files lics := []string{ "Apache-2.0", "GPL-2.0-or-later", "CC0-1.0", } for _, lic := range lics { err = parser.parsePairFromPackage2_3("PackageLicenseInfoFromFiles", lic) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, licWant := range lics { flagFound := false for _, licCheck := range parser.pkg.PackageLicenseInfoFromFiles { if licWant == licCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageLicenseInfoFromFiles", licWant) } } if len(lics) != len(parser.pkg.PackageLicenseInfoFromFiles) { t.Errorf("expected %d licenses in PackageLicenseInfoFromFiles, got %d", len(lics), len(parser.pkg.PackageLicenseInfoFromFiles)) } // Package License Declared err = parser.parsePairFromPackage2_3("PackageLicenseDeclared", "Apache-2.0 OR GPL-2.0-or-later") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseDeclared != "Apache-2.0 OR GPL-2.0-or-later" { t.Errorf("got %v for PackageLicenseDeclared", parser.pkg.PackageLicenseDeclared) } // Package License Comments err = parser.parsePairFromPackage2_3("PackageLicenseComments", "this is a license comment") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageLicenseComments != "this is a license comment" { t.Errorf("got %v for PackageLicenseComments", parser.pkg.PackageLicenseComments) } // Package Copyright Text err = parser.parsePairFromPackage2_3("PackageCopyrightText", "Copyright (c) me myself and i") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageCopyrightText != "Copyright (c) me myself and i" { t.Errorf("got %v for PackageCopyrightText", parser.pkg.PackageCopyrightText) } // Package Summary err = parser.parsePairFromPackage2_3("PackageSummary", "i wrote this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSummary != "i wrote this package" { t.Errorf("got %v for PackageSummary", parser.pkg.PackageSummary) } // Package Description err = parser.parsePairFromPackage2_3("PackageDescription", "i wrote this package a lot") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageDescription != "i wrote this package a lot" { t.Errorf("got %v for PackageDescription", parser.pkg.PackageDescription) } // Package Comment err = parser.parsePairFromPackage2_3("PackageComment", "i scanned this package") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageComment != "i scanned this package" { t.Errorf("got %v for PackageComment", parser.pkg.PackageComment) } // Package Attribution Text attrs := []string{ "Include this notice in all advertising materials", "This is a \nmulti-line string", } for _, attr := range attrs { err = parser.parsePairFromPackage2_3("PackageAttributionText", attr) if err != nil { t.Errorf("expected nil error, got %v", err) } } for _, attrWant := range attrs { flagFound := false for _, attrCheck := range parser.pkg.PackageAttributionTexts { if attrWant == attrCheck { flagFound = true } } if flagFound == false { t.Errorf("didn't find %s in PackageAttributionText", attrWant) } } if len(attrs) != len(parser.pkg.PackageAttributionTexts) { t.Errorf("expected %d attribution texts in PackageAttributionTexts, got %d", len(attrs), len(parser.pkg.PackageAttributionTexts)) } // Package External References and Comments ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Category := "SECURITY" ref1Type := "cpe23Type" ref1Locator := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" ref1Comment := "this is comment #1" ref2 := "OTHER LocationRef-acmeforge acmecorp/acmenator/4.1.3alpha" ref2Category := "OTHER" ref2Type := "LocationRef-acmeforge" ref2Locator := "acmecorp/acmenator/4.1.3alpha" ref2Comment := "this is comment #2" err = parser.parsePairFromPackage2_3("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 1 { t.Errorf("expected 1 external reference, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[0] == nil { t.Errorf("expected non-nil PackageExternalReferences[0], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[0] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[0], got no match") } err = parser.parsePairFromPackage2_3("ExternalRefComment", ref1Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } err = parser.parsePairFromPackage2_3("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if len(parser.pkg.PackageExternalReferences) != 2 { t.Errorf("expected 2 external references, got %d", len(parser.pkg.PackageExternalReferences)) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil pkgExtRef, got nil") } if parser.pkg.PackageExternalReferences[1] == nil { t.Errorf("expected non-nil PackageExternalReferences[1], got nil") } if parser.pkgExtRef != parser.pkg.PackageExternalReferences[1] { t.Errorf("expected pkgExtRef to match PackageExternalReferences[1], got no match") } err = parser.parsePairFromPackage2_3("ExternalRefComment", ref2Comment) if err != nil { t.Errorf("expected nil error, got %v", err) } // finally, check these values gotRef1 := parser.pkg.PackageExternalReferences[0] if gotRef1.Category != ref1Category { t.Errorf("expected ref1 category to be %s, got %s", gotRef1.Category, ref1Category) } if gotRef1.RefType != ref1Type { t.Errorf("expected ref1 type to be %s, got %s", gotRef1.RefType, ref1Type) } if gotRef1.Locator != ref1Locator { t.Errorf("expected ref1 locator to be %s, got %s", gotRef1.Locator, ref1Locator) } if gotRef1.ExternalRefComment != ref1Comment { t.Errorf("expected ref1 comment to be %s, got %s", gotRef1.ExternalRefComment, ref1Comment) } gotRef2 := parser.pkg.PackageExternalReferences[1] if gotRef2.Category != ref2Category { t.Errorf("expected ref2 category to be %s, got %s", gotRef2.Category, ref2Category) } if gotRef2.RefType != ref2Type { t.Errorf("expected ref2 type to be %s, got %s", gotRef2.RefType, ref2Type) } if gotRef2.Locator != ref2Locator { t.Errorf("expected ref2 locator to be %s, got %s", gotRef2.Locator, ref2Locator) } if gotRef2.ExternalRefComment != ref2Comment { t.Errorf("expected ref2 comment to be %s, got %s", gotRef2.ExternalRefComment, ref2Comment) } } func TestParser2_3CanParsePackageSupplierPersonTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Person err := parser.parsePairFromPackage2_3("PackageSupplier", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe" { t.Errorf("got %v for PackageSupplierPerson", parser.pkg.PackageSupplier.Supplier) } } func TestParser2_3CanParsePackageSupplierOrganizationTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: Organization err := parser.parsePairFromPackage2_3("PackageSupplier", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "John Doe, Inc." { t.Errorf("got %v for PackageSupplierOrganization", parser.pkg.PackageSupplier.Supplier) } } func TestParser2_3CanParsePackageSupplierNOASSERTIONTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Supplier: NOASSERTION err := parser.parsePairFromPackage2_3("PackageSupplier", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageSupplier.Supplier != "NOASSERTION" { t.Errorf("got value for Supplier, expected NOASSERTION") } } func TestParser2_3CanParsePackageOriginatorPersonTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Person err := parser.parsePairFromPackage2_3("PackageOriginator", "Person: John Doe") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe" { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParser2_3CanParsePackageOriginatorOrganizationTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: Organization err := parser.parsePairFromPackage2_3("PackageOriginator", "Organization: John Doe, Inc.") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "John Doe, Inc." { t.Errorf("got %v for PackageOriginator", parser.pkg.PackageOriginator.Originator) } } func TestParser2_3CanParsePackageOriginatorNOASSERTIONTag(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Originator: NOASSERTION err := parser.parsePairFromPackage2_3("PackageOriginator", "NOASSERTION") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageOriginator.Originator != "NOASSERTION" { t.Errorf("got false for PackageOriginatorNOASSERTION") } } func TestParser2_3CanParsePackageVerificationCodeTagWithExcludes(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code with excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" err := parser.parsePairFromPackage2_3("PackageVerificationCode", fullCodeValue) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 1 || parser.pkg.PackageVerificationCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParser2_3CanParsePackageVerificationCodeTagWithoutExcludes(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) // Package Verification Code without excludes parenthetical code := "d6a770ba38583ed4bb4525bd96e50461655d2758" err := parser.parsePairFromPackage2_3("PackageVerificationCode", code) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.PackageVerificationCode.Value != code { t.Errorf("got %v for PackageVerificationCode", parser.pkg.PackageVerificationCode) } if len(parser.pkg.PackageVerificationCode.ExcludedFiles) != 0 { t.Errorf("got %v for PackageVerificationCodeExcludedFile", parser.pkg.PackageVerificationCode.ExcludedFiles) } } func TestParser2_3PackageExternalRefPointerChangesAfterTags(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" err := parser.parsePairFromPackage2_3("ExternalRef", ref1) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // now, a comment; pointer should go away err = parser.parsePairFromPackage2_3("ExternalRefComment", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } ref2 := "Other LocationRef-something https://example.com/whatever" err = parser.parsePairFromPackage2_3("ExternalRef", ref2) if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef == nil { t.Errorf("expected non-nil external reference pointer, got nil") } // and some other random tag makes the pointer go away too err = parser.parsePairFromPackage2_3("PackageSummary", "whatever") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkgExtRef != nil { t.Errorf("expected nil external reference pointer, got non-nil") } } func TestParser2_3PackageCreatesRelationshipInDocument(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("Relationship", "SPDXRef-blah CONTAINS SPDXRef-blah-whatever") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.rln == nil { t.Fatalf("parser didn't create and point to Relationship struct") } if parser.rln != parser.doc.Relationships[0] { t.Errorf("pointer to new Relationship doesn't match idx 0 for doc.Relationships[]") } } func TestParser2_3PackageCreatesAnnotationInDocument(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePair2_3("Annotator", "Person: John Doe ()") if err != nil { t.Errorf("got error when calling parsePair2_3: %v", err) } if parser.ann == nil { t.Fatalf("parser didn't create and point to Annotation struct") } if parser.ann != parser.doc.Annotations[0] { t.Errorf("pointer to new Annotation doesn't match idx 0 for doc.Annotations[]") } } func TestParser2_3PackageUnknownTagFails(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: "p1", PackageSPDXIdentifier: "p1"}, } parser.doc.Packages = append(parser.doc.Packages, parser.pkg) err := parser.parsePairFromPackage2_3("blah", "something") if err == nil { t.Errorf("expected error from parsing unknown tag") } } func TestParser2_3FailsIfInvalidSPDXIDInPackageSection(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid ID format err = parser.parsePairFromPackage2_3("SPDXID", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfInvalidPackageSupplierFormat(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier format err = parser.parsePairFromPackage2_3("PackageSupplier", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfUnknownPackageSupplierType(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid supplier type err = parser.parsePairFromPackage2_3("PackageSupplier", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfInvalidPackageOriginatorFormat(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator format err = parser.parsePairFromPackage2_3("PackageOriginator", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfUnknownPackageOriginatorType(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid originator type err = parser.parsePairFromPackage2_3("PackageOriginator", "whoops: John Doe") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3SetsFilesAnalyzedTagsCorrectly(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // set tag err = parser.parsePairFromPackage2_3("FilesAnalyzed", "true") if err != nil { t.Errorf("expected nil error, got %v", err) } if parser.pkg.FilesAnalyzed != true { t.Errorf("expected %v, got %v", true, parser.pkg.FilesAnalyzed) } if parser.pkg.IsFilesAnalyzedTagPresent != true { t.Errorf("expected %v, got %v", true, parser.pkg.IsFilesAnalyzedTagPresent) } } func TestParser2_3FailsIfInvalidPackageChecksumFormat(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum format err = parser.parsePairFromPackage2_3("PackageChecksum", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfInvalidPackageChecksumType(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid checksum type err = parser.parsePairFromPackage2_3("PackageChecksum", "whoops: blah") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfInvalidExternalRefFormat(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // invalid external ref format err = parser.parsePairFromPackage2_3("ExternalRef", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3FailsIfExternalRefCommentBeforeExternalRef(t *testing.T) { parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{}, } // start with Package Name err := parser.parsePairFromPackage2_3("PackageName", "p1") if err != nil { t.Errorf("expected nil error, got %v", err) } // external ref comment before external ref err = parser.parsePairFromPackage2_3("ExternalRefComment", "whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } // ===== Helper function tests ===== func TestCanCheckAndExtractExcludesFilenameAndCode(t *testing.T) { code := "d6a770ba38583ed4bb4525bd96e50461655d2758" fileName := "./package.spdx" fullCodeValue := "d6a770ba38583ed4bb4525bd96e50461655d2758 (excludes: ./package.spdx)" gotCode := extractCodeAndExcludes(fullCodeValue) if gotCode.Value != code { t.Errorf("got %v for gotCode", gotCode) } if len(gotCode.ExcludedFiles) != 1 || gotCode.ExcludedFiles[0] != fileName { t.Errorf("got %v for gotFileName", gotCode.ExcludedFiles) } } func TestCanExtractPackageExternalReference(t *testing.T) { ref1 := "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestCanExtractPackageExternalReferenceWithExtraWhitespace(t *testing.T) { ref1 := " SECURITY \t cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:* \t " category := "SECURITY" refType := "cpe23Type" location := "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*" gotCategory, gotRefType, gotLocation, err := extractPackageExternalReference(ref1) if err != nil { t.Errorf("got non-nil error: %v", err) } if gotCategory != category { t.Errorf("expected category %s, got %s", category, gotCategory) } if gotRefType != refType { t.Errorf("expected refType %s, got %s", refType, gotRefType) } if gotLocation != location { t.Errorf("expected location %s, got %s", location, gotLocation) } } func TestFailsPackageExternalRefWithInvalidFormat(t *testing.T) { _, _, _, err := extractPackageExternalReference("whoops") if err == nil { t.Errorf("expected non-nil error, got nil") } } func TestParser2_3PackageWithoutSpdxIdentifierThrowsError(t *testing.T) { // More than one package, the previous package doesn't contain an SPDX ID pkgOldName := "p1" parser := tvParser2_3{ doc: &v2_3.Document{Packages: []*v2_3.Package{}}, st: psPackage2_3, pkg: &v2_3.Package{PackageName: pkgOldName}, } pkgOld := parser.pkg parser.doc.Packages = append(parser.doc.Packages, pkgOld) // the Document's Packages should have this one only if parser.doc.Packages[0] != pkgOld { t.Errorf("expected package %v, got %v", pkgOld, parser.doc.Packages[0]) } if len(parser.doc.Packages) != 1 { t.Errorf("expected 1 package, got %d", len(parser.doc.Packages)) } pkgName := "p2" err := parser.parsePair2_3("PackageName", pkgName) if err == nil { t.Errorf("package without SPDX Identifier getting accepted") } }