diff --git a/pkg/compliance/bsi.go b/pkg/compliance/bsi.go index 5fed3dc..602430d 100644 --- a/pkg/compliance/bsi.go +++ b/pkg/compliance/bsi.go @@ -164,12 +164,12 @@ func bsiBuildPhase(doc sbom.Document) *record { func bsiSbomDepth(doc sbom.Document) *record { result, score := "", 0.0 // for doc.Components() - totalDependencies := doc.PrimaryComp().Dependencies() + totalDependencies := doc.PrimaryComp().GetTotalNoOfDependencies() if totalDependencies > 0 { score = 10.0 } - result = fmt.Sprintf("doc has %d depedencies", totalDependencies) + result = fmt.Sprintf("doc has %d dependencies", totalDependencies) return newRecordStmt(SBOM_DEPTH, "doc", result, score) } diff --git a/pkg/compliance/ntia.go b/pkg/compliance/ntia.go index 72e62fb..2d01756 100644 --- a/pkg/compliance/ntia.go +++ b/pkg/compliance/ntia.go @@ -30,6 +30,12 @@ var ( validFormats = []string{"json", "xml", "yaml", "yml", "tag-value"} ) +// nolint +const ( + SCORE_FULL = 10.0 + SCORE_ZERO = 0.0 +) + func ntiaResult(ctx context.Context, doc sbom.Document, fileName string, outFormat string) { log := logger.FromContext(ctx) log.Debug("compliance.ntiaResult()") @@ -57,167 +63,155 @@ func ntiaResult(ctx context.Context, doc sbom.Document, fileName string, outForm // format func ntiaAutomationSpec(doc sbom.Document) *record { + result, score := "", SCORE_ZERO spec := doc.Spec().GetSpecType() - result, score := "", 0.0 - fileFormat := doc.Spec().FileFormat() + + result = spec + ", " + fileFormat + if lo.Contains(validFormats, fileFormat) && lo.Contains(validSpec, spec) { result = spec + ", " + fileFormat - score = 10.0 - } else { - result = spec + ", " + fileFormat + score = SCORE_FULL } return newRecordStmt(SBOM_MACHINE_FORMAT, "Automation Support", result, score) } func ntiaSBOMDependency(doc sbom.Document) *record { - result, score := "", 0.0 - // for doc.Components() - totalDependencies := doc.PrimaryComp().Dependencies() + result, score := "", SCORE_ZERO + totalRootDependencies := doc.PrimaryComp().GetTotalNoOfDependencies() - if totalDependencies > 0 { - score = 10.0 + if totalRootDependencies > 0 { + score = SCORE_FULL } - result = fmt.Sprintf("doc has %d depedencies", totalDependencies) + result = fmt.Sprintf("doc has %d dependencies", totalRootDependencies) return newRecordStmt(SBOM_DEPENDENCY, "SBOM Data Fields", result, score) } -// Required Sbom stuffs func ntiaSbomCreator(doc sbom.Document) *record { spec := doc.Spec().GetSpecType() - result, score := "", 0.0 + result, score := "", SCORE_ZERO - if spec == "spdx" { - name, email := "", "" + switch spec { + case "spdx": if tools := doc.Tools(); tools != nil { - for _, tool := range tools { - if name = tool.GetName(); name != "" { - result = name - score = 10.0 - break - } + if toolResult, found := getToolInfo(tools); found { + result = toolResult + score = SCORE_FULL + break } } if authors := doc.Authors(); authors != nil { - for _, author := range authors { - if name = author.GetName(); name != "" { - result = name - score = 10.0 - break - } else if email = author.GetEmail(); email != "" { - result = name - score = 10.0 - break - } + if authorResult, found := getAuthorInfo(authors); found { + result = authorResult + score = SCORE_FULL + break } } - } else if spec == "cyclonedx" { - for _, author := range doc.Authors() { - if author.GetEmail() != "" { - result = author.GetEmail() - score = 10.0 + case "cyclonedx": + if authors := doc.Authors(); authors != nil { + if authorResult, found := getAuthorInfo(authors); found { + result = authorResult + score = SCORE_FULL break } } - if result != "" { return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) } - - tools := doc.Tools() - - for _, tool := range tools { - if name := tool.GetName(); name != "" { - result = name - score = 10.0 + if tools := doc.Tools(); tools != nil { + if toolResult, found := getToolInfo(tools); found { + result = toolResult + score = SCORE_FULL break } } - - supplier := doc.Supplier() - - if supplier != nil { - if supplier.GetEmail() != "" { - result = supplier.GetEmail() - score = 10.0 - } - - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) - } - - if supplier.GetURL() != "" { - result = supplier.GetURL() - score = 10.0 - } - - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) - } - - for _, contact := range supplier.GetContacts() { - if contact.Email() != "" { - result = contact.Email() - score = 10.0 - break - } - } - - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) + if supplier := doc.Supplier(); supplier != nil { + if supplierResult, found := getSupplierInfo(supplier); found { + result = supplierResult + score = SCORE_FULL + break } - } - - manufacturer := doc.Manufacturer() - - if manufacturer != nil { - if manufacturer.GetEmail() != "" { - result = manufacturer.GetEmail() - score = 10.0 - } - - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) + if manufacturer := doc.Manufacturer(); manufacturer != nil { + if manufacturerResult, found := getManufacturerInfo(manufacturer); found { + result = manufacturerResult + score = SCORE_FULL + break } + } + } - if manufacturer.GetURL() != "" { - result = manufacturer.GetURL() - score = 10.0 - } + return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) +} - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) - } +func getManufacturerInfo(manufacturer sbom.Manufacturer) (string, bool) { + if manufacturer == nil { + return "", false + } + if email := manufacturer.GetEmail(); email != "" { + return email, true + } + if url := manufacturer.GetURL(); url != "" { + return url, true + } + for _, contact := range manufacturer.GetContacts() { + if email := contact.Email(); email != "" { + return email, true + } + } + return "", false +} - for _, contact := range manufacturer.GetContacts() { - if contact.Email() != "" { - result = contact.Email() - score = 10.0 - break - } - } +func getSupplierInfo(supplier sbom.GetSupplier) (string, bool) { + if supplier == nil { + return "", false + } + if email := supplier.GetEmail(); email != "" { + return email, true + } + if url := supplier.GetURL(); url != "" { + return url, true + } + for _, contact := range supplier.GetContacts() { + if email := contact.Email(); email != "" { + return email, true + } + } + return "", false +} - if result != "" { - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) - } +func getAuthorInfo(authors []sbom.GetAuthor) (string, bool) { + for _, author := range authors { + if email := author.GetEmail(); email != "" { + return email, true + } + if name := author.GetName(); name != "" { + return name, true + } + } + return "", false +} +func getToolInfo(tools []sbom.GetTool) (string, bool) { + for _, tool := range tools { + if name := tool.GetName(); name != "" { + return name, true } } - return newRecordStmt(SBOM_CREATOR, "SBOM Data Fields", result, score) + return "", false } func ntiaSbomCreatedTimestamp(doc sbom.Document) *record { - score := 0.0 + score := SCORE_ZERO result := doc.Spec().GetCreationTimestamp() if result != "" { _, err := time.Parse(time.RFC3339, result) if err != nil { - score = 0.0 + score = SCORE_ZERO } else { - score = 10.0 + score = SCORE_FULL } } return newRecordStmt(SBOM_TIMESTAMP, "SBOM Data Fields", result, score) @@ -239,7 +233,7 @@ func ntiaComponents(doc sbom.Document) []*record { records := []*record{} if len(doc.Components()) == 0 { - records = append(records, newRecordStmt(SBOM_COMPONENTS, "SBOM Data Fields", "absent", 0.0)) + records = append(records, newRecordStmt(SBOM_COMPONENTS, "SBOM Data Fields", "absent", SCORE_ZERO)) return records } @@ -260,118 +254,66 @@ func ntiaComponents(doc sbom.Document) []*record { func ntiaComponentName(component sbom.GetComponent) *record { if result := component.GetName(); result != "" { - return newRecordStmt(COMP_NAME, component.GetName(), result, 10.0) + return newRecordStmt(COMP_NAME, component.GetName(), result, SCORE_FULL) } - return newRecordStmt(COMP_NAME, component.GetName(), "", 0.0) + return newRecordStmt(COMP_NAME, component.GetName(), "", SCORE_ZERO) } func ntiaComponentCreator(doc sbom.Document, component sbom.GetComponent) *record { spec := doc.Spec().GetSpecType() - - if spec == "spdx" { - if supplier := component.Suppliers().GetEmail(); supplier != "" { - return newRecordStmt(PACK_SUPPLIER, component.GetName(), supplier, 10.0) - } - } else if spec == "cyclonedx" { - result := "" - score := 0.0 - - supplier := component.Suppliers() - if supplier != nil { - if supplier.GetEmail() != "" { - result = supplier.GetEmail() - score = 10.0 - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) - } - - if supplier.GetURL() != "" { - result = supplier.GetURL() - score = 10.0 - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) - } - - if supplier.GetContacts() != nil { - for _, contact := range supplier.GetContacts() { - if contact.Email() != "" { - result = contact.Email() - score = 10.0 - break - } - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) - } + result, score := "", SCORE_ZERO + + switch spec { + case "spdx": + if supplier := component.Suppliers(); supplier != nil { + if supplierResult, found := getSupplierInfo(supplier); found { + result = supplierResult + score = SCORE_FULL + break } } - - manufacturer := component.Manufacturer() - - if manufacturer != nil { - if manufacturer.GetEmail() != "" { - result = manufacturer.GetEmail() - score = 10.0 - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) - } - - if manufacturer.GetEmail() != "" { - result = manufacturer.GetURL() - score = 10.0 - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) + case "cyclonedx": + if supplier := component.Suppliers(); supplier != nil { + if supplierResult, found := getSupplierInfo(supplier); found { + result = supplierResult + score = SCORE_FULL + break } + } - if manufacturer.GetContacts() != nil { - for _, contact := range manufacturer.GetContacts() { - if contact.Email() != "" { - result = contact.Email() - score = 10.0 - break - } - } - - if result != "" { - return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) - } + if manufacturer := component.Manufacturer(); manufacturer != nil { + if manufacturerResult, found := getManufacturerInfo(manufacturer); found { + result = manufacturerResult + score = SCORE_FULL + break } } } - return newRecordStmt(COMP_CREATOR, component.GetName(), "", 0.0) + return newRecordStmt(COMP_CREATOR, component.GetName(), result, score) } func ntiaComponentVersion(component sbom.GetComponent) *record { result := component.GetVersion() if result != "" { - return newRecordStmt(COMP_VERSION, component.GetName(), result, 10.0) + return newRecordStmt(COMP_VERSION, component.GetName(), result, SCORE_FULL) } - return newRecordStmt(COMP_VERSION, component.GetName(), "", 0.0) + return newRecordStmt(COMP_VERSION, component.GetName(), "", SCORE_ZERO) } func ntiaComponentDependencies(doc sbom.Document, component sbom.GetComponent) *record { - result, score := "", 0.0 + result, score := "", SCORE_ZERO var results []string dependencies := doc.GetRelationships(component.GetID()) if dependencies == nil { - return newRecordStmt(COMP_DEPTH, component.GetName(), "no-relationships", 0.0) + return newRecordStmt(COMP_DEPTH, component.GetName(), "no-relationships", SCORE_ZERO) } for _, d := range dependencies { componentName := extractName(d) results = append(results, componentName) - score = 10.0 + score = SCORE_FULL } if results != nil { @@ -387,7 +329,7 @@ func ntiaComponentOtherUniqIDs(doc sbom.Document, component sbom.GetComponent) * spec := doc.Spec().GetSpecType() if spec == "spdx" { - result, score, totalElements, containPurlElement := "", 0.0, 0, 0 + result, score, totalElements, containPurlElement := "", SCORE_ZERO, 0, 0 if extRefs := component.ExternalReferences(); extRefs != nil { for _, extRef := range extRefs { @@ -399,34 +341,31 @@ func ntiaComponentOtherUniqIDs(doc sbom.Document, component sbom.GetComponent) * } } if containPurlElement != 0 { - score = (float64(containPurlElement) / float64(totalElements)) * 10.0 + score = (float64(containPurlElement) / float64(totalElements)) * SCORE_FULL x := fmt.Sprintf(":(%d/%d)", containPurlElement, totalElements) result = result + x } return newRecordStmt(COMP_OTHER_UNIQ_IDS, component.GetName(), result, score) } else if spec == "cyclonedx" { result := "" - score := 0.0 purl := component.GetPurls() if len(purl) > 0 { result = string(purl[0]) - score = 10.0 - return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), result, score) + return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), result, SCORE_FULL) } cpes := component.GetCpes() if len(cpes) > 0 { result = string(cpes[0]) - score = 10.0 - return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), result, score) + return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), result, SCORE_FULL) } - return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), "", 0.0) + return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetName(), "", SCORE_ZERO) } - return newRecordStmt(COMP_OTHER_UNIQ_IDS, component.GetName(), "", 0.0) + return newRecordStmt(COMP_OTHER_UNIQ_IDS, component.GetName(), "", SCORE_ZERO) } diff --git a/pkg/compliance/ntia_test.go b/pkg/compliance/ntia_test.go index b037ccf..e2068a1 100644 --- a/pkg/compliance/ntia_test.go +++ b/pkg/compliance/ntia_test.go @@ -23,7 +23,8 @@ func createSpdxDummyDocumentNtia() sbom.Document { pack := sbom.NewComponent() pack.Version = "v0.7.1" - pack.Name = "core-js" + pack.Name = "tool-golang" + pack.ID = "github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1" supplier := sbom.Supplier{ Email: "hello@interlynk.io", @@ -34,6 +35,9 @@ func createSpdxDummyDocumentNtia() sbom.Document { RefType: "purl", } + var primary sbom.PrimaryComp + primary.Dependecies = 1 + var externalReferences []sbom.GetExternalReference externalReferences = append(externalReferences, extRef) pack.ExternalRefs = externalReferences @@ -41,18 +45,16 @@ func createSpdxDummyDocumentNtia() sbom.Document { var packages []sbom.GetComponent packages = append(packages, pack) - depend := sbom.Relation{ - From: "github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1", - To: "github/spdx/gordf@b735bd5aac89fe25cad4ef488a95bc00ea549edd", - } - var dependencies []sbom.GetRelation - dependencies = append(dependencies, depend) + relationships := make(map[string][]string) + relationships["github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1"] = append(relationships["github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1"], "github/spdx/gordf@b735bd5aac89fe25cad4ef488a95bc00ea549edd") + CompIDWithName["github/spdx/gordf@b735bd5aac89fe25cad4ef488a95bc00ea549edd"] = "gordf" doc := sbom.SpdxDoc{ - SpdxSpec: s, - Comps: packages, - SpdxTools: creators, - Rels: dependencies, + SpdxSpec: s, + Comps: packages, + SpdxTools: creators, + Dependencies: relationships, + PrimaryComponent: primary, } return doc } @@ -101,13 +103,24 @@ func TestNtiaSpdxSbomPass(t *testing.T) { id: "SBOM Data Fields", }, }, + { + name: "SbomDependency", + actual: ntiaSBOMDependency(doc), + expected: desiredNtia{ + score: 10.0, + result: "doc has 1 dependencies", + key: SBOM_DEPENDENCY, + id: "SBOM Data Fields", + }, + }, + { name: "ComponentCreator", actual: ntiaComponentCreator(doc, doc.Components()[0]), expected: desiredNtia{ score: 10.0, result: "hello@interlynk.io", - key: PACK_SUPPLIER, + key: COMP_CREATOR, id: doc.Components()[0].GetName(), }, }, @@ -117,7 +130,7 @@ func TestNtiaSpdxSbomPass(t *testing.T) { actual: ntiaComponentName(doc.Components()[0]), expected: desiredNtia{ score: 10.0, - result: "core-js", + result: "tool-golang", key: COMP_NAME, id: doc.Components()[0].GetName(), }, @@ -177,7 +190,8 @@ func createCdxDummyDocumentNtia() sbom.Document { comp := sbom.NewComponent() comp.Version = "v0.7.1" - comp.Name = "core-js" + comp.Name = "tool-golang" + comp.ID = "github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1" supplier := sbom.Supplier{ Email: "hello@interlynk.io", @@ -199,10 +213,20 @@ func createCdxDummyDocumentNtia() sbom.Document { var components []sbom.GetComponent components = append(components, comp) + relationships := make(map[string][]string) + relationships["github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1"] = append(relationships["github/spdx/tools-golang@9db247b854b9634d0109153d515fd1a9efd5a1b1"], "github/spdx/gordf@b735bd5aac89fe25cad4ef488a95bc00ea549edd") + + var primary sbom.PrimaryComp + primary.Dependecies = 1 + + CompIDWithName["github/spdx/gordf@b735bd5aac89fe25cad4ef488a95bc00ea549edd"] = "gordf" + doc := sbom.CdxDoc{ - CdxSpec: cdxSpec, - Comps: components, - CdxAuthors: authors, + CdxSpec: cdxSpec, + Comps: components, + CdxAuthors: authors, + Dependencies: relationships, + PrimaryComponent: primary, } return doc } @@ -244,6 +268,16 @@ func TestNtiaCdxSbomPass(t *testing.T) { id: "SBOM Data Fields", }, }, + { + name: "SbomDependency", + actual: ntiaSBOMDependency(doc), + expected: desiredNtia{ + score: 10.0, + result: "doc has 1 dependencies", + key: SBOM_DEPENDENCY, + id: "SBOM Data Fields", + }, + }, { name: "ComponentCreator", actual: ntiaComponentCreator(doc, doc.Components()[0]), @@ -251,7 +285,7 @@ func TestNtiaCdxSbomPass(t *testing.T) { score: 10.0, result: "hello@interlynk.io", key: COMP_CREATOR, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -259,9 +293,9 @@ func TestNtiaCdxSbomPass(t *testing.T) { actual: ntiaComponentName(doc.Components()[0]), expected: desiredNtia{ score: 10.0, - result: "core-js", + result: "tool-golang", key: COMP_NAME, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -271,7 +305,7 @@ func TestNtiaCdxSbomPass(t *testing.T) { score: 10.0, result: "v0.7.1", key: COMP_VERSION, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -281,7 +315,17 @@ func TestNtiaCdxSbomPass(t *testing.T) { score: 10.0, result: "vivek", key: COMP_OTHER_UNIQ_IDS, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), + }, + }, + { + name: "ComponentDependencies", + actual: ntiaComponentDependencies(doc, doc.Components()[0]), + expected: desiredNtia{ + score: 10.0, + result: "gordf", + key: COMP_DEPTH, + id: doc.Components()[0].GetName(), }, }, } diff --git a/pkg/compliance/oct_test.go b/pkg/compliance/oct_test.go index 7a42ac4..2655a4b 100644 --- a/pkg/compliance/oct_test.go +++ b/pkg/compliance/oct_test.go @@ -72,6 +72,7 @@ func createDummyDocument() sbom.Document { } type desired struct { + name string score float64 result string key int @@ -87,6 +88,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSpec(doc), expected: desired{ + name: "octSpec", score: 10.0, result: "spdx", key: SBOM_SPEC, @@ -96,6 +98,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomName(doc), expected: desired{ + name: "octSbomName", score: 10.0, result: "nano", key: SBOM_NAME, @@ -105,6 +108,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomNamespace(doc), expected: desired{ + name: "octSbomNamespace", score: 10.0, result: "https://anchore.com/syft/dir/sbomqs-6ec18b03-96cb-4951-b299-929890c1cfc8", key: SBOM_NAMESPACE, @@ -114,6 +118,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomOrganization(doc), expected: desired{ + name: "octSbomOrganization", score: 10.0, result: "interlynk", key: SBOM_ORG, @@ -123,6 +128,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomComment(doc), expected: desired{ + name: "octSbomComment", score: 10.0, result: "this is a general sbom created using syft tool", key: SBOM_COMMENT, @@ -132,6 +138,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomTool(doc), expected: desired{ + name: "octSbomTool", score: 10.0, result: "syft", key: SBOM_TOOL, @@ -141,6 +148,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSbomLicense(doc), expected: desired{ + name: "octSbomLicense", score: 10.0, result: "cc0-1.0", key: SBOM_LICENSE, @@ -150,6 +158,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSpecVersion(doc), expected: desired{ + name: "octSpecVersion", score: 10.0, result: "SPDX-2.3", key: SBOM_SPEC_VERSION, @@ -159,6 +168,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octCreatedTimestamp(doc), expected: desired{ + name: "octCreatedTimestamp", score: 10.0, result: "2023-05-04T09:33:40Z", key: SBOM_TIMESTAMP, @@ -168,6 +178,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octSpecSpdxID(doc), expected: desired{ + name: "octSpecSpdxID", score: 10.0, result: "DOCUMENT", key: SBOM_SPDXID, @@ -178,6 +189,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octMachineFormat(doc), expected: desired{ + name: "octMachineFormat", score: 10.0, result: "spdx, json", key: SBOM_MACHINE_FORMAT, @@ -187,6 +199,7 @@ func TestOctSbomPass(t *testing.T) { { actual: octHumanFormat(doc), expected: desired{ + name: "octHumanFormat", score: 10.0, result: "json", key: SBOM_HUMAN_FORMAT, @@ -196,100 +209,111 @@ func TestOctSbomPass(t *testing.T) { { actual: octPackageName(doc.Components()[0]), expected: desired{ + name: "octPackageName", score: 10.0, result: "core-js", key: PACK_NAME, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageVersion(doc.Components()[0]), expected: desired{ + name: "octPackageVersion", score: 10.0, result: "v0.7.1", key: PACK_VERSION, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageSpdxID(doc.Components()[0]), expected: desired{ + name: "octPackageSpdxID", score: 10.0, result: "SPDXRef-npm-core-js-3.6.5", key: PACK_SPDXID, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageSupplier(doc.Components()[0]), expected: desired{ + name: "octPackageSupplier", score: 10.0, result: "vivekkumarsahu650@gmail.com", key: PACK_SUPPLIER, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageHash(doc.Components()[0]), expected: desired{ + name: "octPackageHash", score: 10.0, result: "ee1300ac533cebc2d070ce3765685d5f7fca2a5a78ca15068323f68ed63d4abf", key: PACK_HASH, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageExternalRefs(doc.Components()[0]), expected: desired{ + name: "octPackageExternalRefs", score: 10.0, result: "purl:(1/1)", key: PACK_EXT_REF, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageCopyright(doc.Components()[0]), expected: desired{ + name: "octPackageCopyright", score: 10.0, result: "Copyright 2001-2011 The Apache Software Foundation", key: PACK_COPYRIGHT, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageFileAnalyzed(doc.Components()[0]), expected: desired{ + name: "octPackageFileAnalyzed", score: 10.0, result: "yes", key: PACK_FILE_ANALYZED, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageConLicense(doc.Components()[0]), expected: desired{ + name: "octPackageConLicense", score: 10.0, result: "(LGPL-2.0-only OR LicenseRef-3)", key: PACK_LICENSE_CON, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageDecLicense(doc.Components()[0]), expected: desired{ + name: "octPackageDecLicense", score: 10.0, result: "(LGPL-2.0-only AND LicenseRef-3)", key: PACK_LICENSE_DEC, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { actual: octPackageDownloadURL(doc.Components()[0]), expected: desired{ + name: "octPackageDownloadURL", score: 10.0, result: "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", key: PACK_DOWNLOAD_URL, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, } @@ -485,7 +509,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_NAME, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -494,7 +518,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_VERSION, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -503,7 +527,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_SPDXID, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -512,7 +536,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_SUPPLIER, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -521,7 +545,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_HASH, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -530,7 +554,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "cpe23Type", key: PACK_EXT_REF, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -539,7 +563,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "NOASSERTION", key: PACK_COPYRIGHT, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -548,7 +572,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "no", key: PACK_FILE_ANALYZED, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -557,7 +581,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "NONE", key: PACK_LICENSE_CON, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -566,7 +590,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "NOASSERTION", key: PACK_LICENSE_DEC, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, { @@ -575,7 +599,7 @@ func TestOctSbomFail(t *testing.T) { score: 0.0, result: "", key: PACK_DOWNLOAD_URL, - id: doc.Components()[0].GetID(), + id: doc.Components()[0].GetName(), }, }, } diff --git a/pkg/engine/score_test.go b/pkg/engine/score_test.go index 01d8638..ee61771 100644 --- a/pkg/engine/score_test.go +++ b/pkg/engine/score_test.go @@ -105,4 +105,4 @@ func TestProcessURL(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/pkg/sbom/cdx.go b/pkg/sbom/cdx.go index 23aa5d2..135f4e6 100644 --- a/pkg/sbom/cdx.go +++ b/pkg/sbom/cdx.go @@ -36,22 +36,22 @@ var ( ) type CdxDoc struct { - doc *cydx.BOM - format FileFormat - ctx context.Context - CdxSpec *Specs - Comps []GetComponent - CdxAuthors []GetAuthor - CdxTools []GetTool - rels []GetRelation - logs []string - lifecycles []string - supplier GetSupplier - manufacturer Manufacturer - compositions map[string]string - primaryComp primaryComp - dependencies map[string][]string - composition map[string]string + doc *cydx.BOM + format FileFormat + ctx context.Context + CdxSpec *Specs + Comps []GetComponent + CdxAuthors []GetAuthor + CdxTools []GetTool + rels []GetRelation + logs []string + lifecycles []string + supplier GetSupplier + manufacturer Manufacturer + compositions map[string]string + PrimaryComponent PrimaryComp + Dependencies map[string][]string + composition map[string]string } func newCDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Document, error) { @@ -91,8 +91,8 @@ func newCDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Documen return doc, err } -func (c CdxDoc) PrimaryComp() PrimaryComp { - return &c.primaryComp +func (c CdxDoc) PrimaryComp() GetPrimaryComp { + return &c.PrimaryComponent } func (c CdxDoc) Spec() Spec { @@ -132,7 +132,7 @@ func (c CdxDoc) Manufacturer() Manufacturer { } func (c CdxDoc) GetRelationships(componentID string) []string { - return c.dependencies[componentID] + return c.Dependencies[componentID] } func (c CdxDoc) GetComposition(componentID string) string { @@ -293,7 +293,7 @@ func copyC(cdxc *cydx.Component, c *CdxDoc) *Component { } } - if cdxc.BOMRef == c.primaryComp.id { + if cdxc.BOMRef == c.PrimaryComponent.ID { nc.isPrimary = true } @@ -520,10 +520,10 @@ func (c *CdxDoc) parsePrimaryCompAndRelationships() { return } - c.dependencies = make(map[string][]string) + c.Dependencies = make(map[string][]string) - c.primaryComp.present = true - c.primaryComp.id = c.doc.Metadata.Component.BOMRef + c.PrimaryComponent.Present = true + c.PrimaryComponent.ID = c.doc.Metadata.Component.BOMRef var totalDependencies int c.rels = []GetRelation{} @@ -533,20 +533,21 @@ func (c *CdxDoc) parsePrimaryCompAndRelationships() { nr := Relation{} nr.From = r.Ref nr.To = d - if r.Ref == c.primaryComp.id { - c.primaryComp.hasDependencies = true + if r.Ref == c.PrimaryComponent.ID { + c.PrimaryComponent.hasDependencies = true totalDependencies++ c.rels = append(c.rels, nr) - c.dependencies[c.primaryComp.id] = append(c.dependencies[c.primaryComp.id], d) + c.Dependencies[c.PrimaryComponent.ID] = append(c.Dependencies[c.PrimaryComponent.ID], d) } else { c.rels = append(c.rels, nr) - c.dependencies[r.Ref] = append(c.dependencies[r.Ref], d) + c.Dependencies[r.Ref] = append(c.Dependencies[r.Ref], d) } } } - c.primaryComp.dependecies = totalDependencies + c.PrimaryComponent.Dependecies = totalDependencies } +// nolint func (c *CdxDoc) parseComposition() { if c.doc.Metadata == nil { return @@ -562,6 +563,7 @@ func (c *CdxDoc) parseComposition() { } } +// nolint func compNormalise(compID string) string { switch cydx.CompositionAggregate(compID) { case cydx.CompositionAggregateComplete: diff --git a/pkg/sbom/document.go b/pkg/sbom/document.go index 2a4d122..50b9458 100644 --- a/pkg/sbom/document.go +++ b/pkg/sbom/document.go @@ -29,6 +29,6 @@ type Document interface { Manufacturer() Manufacturer Supplier() GetSupplier - PrimaryComp() PrimaryComp + PrimaryComp() GetPrimaryComp GetRelationships(string) []string } diff --git a/pkg/sbom/primarycomp.go b/pkg/sbom/primarycomp.go index b856dc7..b1ebfe2 100644 --- a/pkg/sbom/primarycomp.go +++ b/pkg/sbom/primarycomp.go @@ -14,31 +14,31 @@ package sbom -type PrimaryComp interface { - Present() bool - ID() string - Dependencies() int +type GetPrimaryComp interface { + IsPresent() bool + GetID() string + GetTotalNoOfDependencies() int } -type primaryComp struct { - present bool - id string - dependecies int +type PrimaryComp struct { + Present bool + ID string + Dependecies int hasDependencies bool } -func (pc *primaryComp) Present() bool { - return pc.present +func (pc *PrimaryComp) IsPresent() bool { + return pc.Present } -func (pc *primaryComp) ID() string { - return pc.id +func (pc *PrimaryComp) GetID() string { + return pc.ID } -func (pc *primaryComp) Dependencies() int { - return pc.dependecies +func (pc *PrimaryComp) GetTotalNoOfDependencies() int { + return pc.Dependecies } -func (pc *primaryComp) HasDependencies() bool { +func (pc *PrimaryComp) HasDependencies() bool { return pc.hasDependencies } diff --git a/pkg/sbom/spdx.go b/pkg/sbom/spdx.go index c7e0c6b..3770531 100644 --- a/pkg/sbom/spdx.go +++ b/pkg/sbom/spdx.go @@ -43,19 +43,19 @@ var ( ) type SpdxDoc struct { - doc *spdx.Document - format FileFormat - ctx context.Context - SpdxSpec *Specs - Comps []GetComponent - authors []GetAuthor - SpdxTools []GetTool - Rels []GetRelation - logs []string - primaryComp primaryComp - lifecycles string - dependencies map[string][]string - composition map[string]string + doc *spdx.Document + format FileFormat + ctx context.Context + SpdxSpec *Specs + Comps []GetComponent + authors []GetAuthor + SpdxTools []GetTool + Rels []GetRelation + logs []string + PrimaryComponent PrimaryComp + lifecycles string + Dependencies map[string][]string + composition map[string]string } func newSPDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Document, error) { @@ -97,8 +97,8 @@ func newSPDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Docume return doc, err } -func (c SpdxDoc) PrimaryComp() PrimaryComp { - return &c.primaryComp +func (s SpdxDoc) PrimaryComp() GetPrimaryComp { + return &s.PrimaryComponent } func (s SpdxDoc) Spec() Spec { @@ -138,7 +138,7 @@ func (s SpdxDoc) Supplier() GetSupplier { } func (s SpdxDoc) GetRelationships(componentID string) []string { - return s.dependencies[componentID] + return s.Dependencies[componentID] } func (s SpdxDoc) GetComposition(componentID string) string { @@ -244,7 +244,7 @@ func (s *SpdxDoc) parseComps() { nc.DownloadLocation = sc.PackageDownloadLocation } - nc.isPrimary = s.primaryComp.id == string(sc.PackageSPDXIdentifier) + nc.isPrimary = s.PrimaryComponent.ID == string(sc.PackageSPDXIdentifier) fromRelsPresent := func(rels []GetRelation, id string) bool { for _, r := range rels { @@ -288,7 +288,7 @@ func (s *SpdxDoc) parseAuthors() { func (s *SpdxDoc) parsePrimaryCompAndRelationships() { s.Rels = []GetRelation{} - s.dependencies = make(map[string][]string) + s.Dependencies = make(map[string][]string) var err error var aBytes, bBytes []byte var primaryComponent string @@ -302,22 +302,17 @@ func (s *SpdxDoc) parsePrimaryCompAndRelationships() { continue } primaryComponent = string(bBytes) - s.primaryComp.id = primaryComponent - s.primaryComp.present = true + s.PrimaryComponent.ID = primaryComponent + s.PrimaryComponent.Present = true } } - // if s.primaryComp.present { for _, r := range s.doc.Relationships { if strings.ToUpper(r.Relationship) == spdx_common.TypeRelationshipDependsOn { aBytes, err = r.RefA.MarshalJSON() if err != nil { continue } - // bBytes, err = r.RefB.ElementRefID.MarshalJSON() - // if err != nil { - // continue - // } if string(aBytes) == primaryComponent { bBytes, err = r.RefB.MarshalJSON() @@ -332,105 +327,19 @@ func (s *SpdxDoc) parsePrimaryCompAndRelationships() { totalDependencies++ s.Rels = append(s.Rels, nr) - s.dependencies[primaryComponent] = append(s.dependencies[primaryComponent], string(bBytes)) + s.Dependencies[primaryComponent] = append(s.Dependencies[primaryComponent], string(bBytes)) } else { nr := Relation{ From: string(aBytes), To: string(bBytes), } - s.dependencies[string(aBytes)] = append(s.dependencies[string(aBytes)], string(bBytes)) + s.Dependencies[string(aBytes)] = append(s.Dependencies[string(aBytes)], string(bBytes)) s.Rels = append(s.Rels, nr) } } } - // } -} - -// func (s *SpdxDoc) parseComponentDepedencies() { -// s.Rels = []GetRelation{} -// s.dependencies = make(map[string][]string) -// var err error -// var aBytes, bBytes []byte - -// for _, r := range s.doc.Relationships { -// if strings.ToUpper(r.Relationship) == spdx_common.TypeRelationshipDependsOn { -// aBytes, err = r.RefA.MarshalJSON() -// if err != nil { -// continue -// } -// bBytes, err = r.RefB.ElementRefID.MarshalJSON() -// if err != nil { -// continue -// } - -// nr := Relation{ -// From: string(aBytes), -// To: string(bBytes), -// } -// s.Rels = append(s.Rels, nr) -// s.dependencies[string(aBytes)] = append(s.dependencies[string(aBytes)], string(bBytes)) -// } -// } -// } - -// func (s *SpdxDoc) parseRelsAndPrimaryComp() { -// s.Rels = []GetRelation{} -// var err error -// var aBytes, bBytes []byte -// var primaryComponent string -// var totalDependencies int - -// for _, r := range s.doc.Relationships { -// if strings.ToUpper(r.Relationship) == spdx_common.TypeRelationshipDescribe { -// bBytes, err = r.RefB.ElementRefID.MarshalJSON() -// if err != nil { -// continue -// } -// primaryComponent = string(bBytes) -// s.primaryComp.id = primaryComponent -// s.primaryComp.present = true -// } -// } -// // If no primary component found, return early -// if primaryComponent == "" { -// return -// } - -// for _, r := range s.doc.Relationships { -// if strings.ToUpper(r.Relationship) == spdx_common.TypeRelationshipDependsOn { -// aBytes, err = r.RefA.MarshalJSON() -// if err != nil { -// continue -// } -// bBytes, err = r.RefB.ElementRefID.MarshalJSON() -// if err != nil { -// continue -// } - -// if string(aBytes) == primaryComponent { -// bBytes, err = r.RefB.MarshalJSON() -// if err != nil { -// continue -// } - -// nr := Relation{ -// From: primaryComponent, -// To: string(bBytes), -// } -// totalDependencies++ - -// s.Rels = append(s.Rels, nr) -// } else { -// nr := Relation{ -// From: string(aBytes), -// To: string(bBytes), -// } -// s.Rels = append(s.Rels, nr) -// } -// } -// } -// s.primaryComp.dependecies = totalDependencies -// } + s.PrimaryComponent.Dependecies = totalDependencies +} // creationInfo.Creators.Tool // Also create for org: , creationInfo.Creators.Organization diff --git a/pkg/scorer/ntia.go b/pkg/scorer/ntia.go index 0c03c23..9a5f552 100644 --- a/pkg/scorer/ntia.go +++ b/pkg/scorer/ntia.go @@ -116,7 +116,7 @@ func compWithUniqIDCheck(d sbom.Document, c *check) score { func docWithDepedenciesCheck(d sbom.Document, c *check) score { s := newScoreFromCheck(c) - totalDependencies := d.PrimaryComp().Dependencies() + totalDependencies := d.PrimaryComp().GetTotalNoOfDependencies() if totalDependencies > 0 { s.setScore(10.0) } diff --git a/pkg/scorer/quality.go b/pkg/scorer/quality.go index 7da7aff..4ee96e9 100644 --- a/pkg/scorer/quality.go +++ b/pkg/scorer/quality.go @@ -231,7 +231,7 @@ func docWithCreatorCheck(d sbom.Document, c *check) score { func docWithPrimaryComponentCheck(d sbom.Document, c *check) score { s := newScoreFromCheck(c) - if d.PrimaryComp().Present() { + if d.PrimaryComp().IsPresent() { s.setScore(10.0) s.setDesc("primary component found") return *s