diff --git a/cmd/sbom-scorecard/cmd/scorecard.go b/cmd/sbom-scorecard/cmd/scorecard.go index 11c66cf..1a0ce2e 100644 --- a/cmd/sbom-scorecard/cmd/scorecard.go +++ b/cmd/sbom-scorecard/cmd/scorecard.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "io/ioutil" "os" "errors" @@ -34,7 +33,7 @@ func init() { } func determineSbomType(filepath string) string { - content, err := ioutil.ReadFile(filepath) + content, err := os.ReadFile(filepath) if err != nil { panic(fmt.Sprintf("Error! %v", err)) } diff --git a/examples/invalid.json b/examples/invalid.json new file mode 100644 index 0000000..2af0279 --- /dev/null +++ b/examples/invalid.json @@ -0,0 +1,3 @@ +{ + "makeJSON": "happy" +}This is intentionally invalid diff --git a/pkg/cdx/cdx_report.go b/pkg/cdx/cdx_report.go index 1eda860..98b4f96 100644 --- a/pkg/cdx/cdx_report.go +++ b/pkg/cdx/cdx_report.go @@ -3,7 +3,7 @@ package cdx import ( "bytes" "fmt" - "io/ioutil" + "os" "strings" cdx "github.com/CycloneDX/cyclonedx-go" @@ -25,6 +25,11 @@ type CycloneDXReport struct { hasCPE int } +var missingPackages = scorecard.ReportValue{ + Ratio: 0, + Reasoning: "No packages", +} + func (r *CycloneDXReport) Report() string { var sb strings.Builder sb.WriteString(fmt.Sprintf("%d total packages\n", r.totalPackages)) @@ -57,6 +62,9 @@ func (r *CycloneDXReport) IsSpecCompliant() scorecard.ReportValue { } func (r *CycloneDXReport) PackageIdentification() scorecard.ReportValue { + if r.totalPackages == 0 { + return missingPackages + } purlPercent := scorecard.PrettyPercent(r.hasPurl, r.totalPackages) cpePercent := scorecard.PrettyPercent(r.hasCPE, r.totalPackages) return scorecard.ReportValue{ @@ -90,7 +98,7 @@ func (r *CycloneDXReport) CreationInfo() scorecard.ReportValue { } func GetCycloneDXReport(filename string) scorecard.SbomReport { - contents, err := ioutil.ReadFile(filename) + contents, err := os.ReadFile(filename) if err != nil { fmt.Printf("Error while opening %v for reading: %v", filename, err) return nil @@ -116,7 +124,7 @@ func GetCycloneDXReport(filename string) scorecard.SbomReport { return &r } - if bom.Metadata.Tools != nil { + if bom.Metadata != nil && bom.Metadata.Tools != nil { for _, t := range *bom.Metadata.Tools { if t.Name != "" { r.creationToolName += 1 diff --git a/pkg/cdx/cdx_report_test.go b/pkg/cdx/cdx_report_test.go index c7fe4b8..abaf0fd 100644 --- a/pkg/cdx/cdx_report_test.go +++ b/pkg/cdx/cdx_report_test.go @@ -58,3 +58,17 @@ Package Licenses: 18/20 Creation Info: 15/15 Total points: 88/100 or 88%`) } + +func TestCycloneInvalid(t *testing.T) { + r := GetCycloneDXReport("../../examples/invalid.json") + + report_text := scorecard.Grade(r) + assertTextEqual(t, + report_text, + `Spec Compliance: 0/25 +Package ID: 0/20 (0% have purls and 0% have CPEs) +Package Versions: 0/20 +Package Licenses: 0/20 +Creation Info: 0/15 +Total points: 0/100 or 0%`) +} diff --git a/pkg/scorecard/scorecard.go b/pkg/scorecard/scorecard.go index 40e73d5..2a41887 100644 --- a/pkg/scorecard/scorecard.go +++ b/pkg/scorecard/scorecard.go @@ -32,8 +32,14 @@ type ScoreValue struct { MaxPoints float32 } +func isNaN(f float32) bool { return f != f } + func (sv *ScoreValue) Score() float32 { - return sv.Ratio * sv.MaxPoints + if isNaN(sv.Ratio) { + return 0 + } else { + return sv.Ratio * sv.MaxPoints + } } type ReportResult struct { diff --git a/pkg/spdx/document.go b/pkg/spdx/document.go index 0e90dd4..a5f0a0d 100644 --- a/pkg/spdx/document.go +++ b/pkg/spdx/document.go @@ -6,7 +6,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "os" spdx_json "github.com/spdx/tools-golang/json" spdx_rdf "github.com/spdx/tools-golang/rdfloader" @@ -37,7 +37,7 @@ type File struct { } func LoadDocument(path string) (Document, error) { - f, err := ioutil.ReadFile(path) + f, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("opening SPDX document: %w", err) }