Repository: eBay/sbom-scorecard (language: go)

Issue:
[Potential Bug] cyclonedx logic on package versions uses package digest
I noticed that the `PackageVersions` function uses package digests (`hasPackDigest`). Is this intended? 


https://github.com/eBay/sbom-scorecard/blob/712e7661d06c61309c1794e761f3faf095a69de6/pkg/cdx/cdx_report.go#L66-L70

The underlying `Component` struct has a `Version` field. I had expected the `PakageVersions` function to use the `Version` field. I'm glad to put in a PR to fix this if this is not the intended behavior.


Discussion / hints:
So the intent was: "are packages identifiable by a version?" Ideally, we'd use a version field, but I'll admit that there's currently some ambiguity in the package around digest versus versioning. I welcome your change.

Fix patch:
diff --git a/pkg/cdx/cdx_report.go b/pkg/cdx/cdx_report.go
index bdec7b8..1eda860 100644
--- a/pkg/cdx/cdx_report.go
+++ b/pkg/cdx/cdx_report.go
@@ -17,17 +17,19 @@ type CycloneDXReport struct {
 	creationToolName    int
 	creationToolVersion int
 
-	totalPackages int
-	hasLicense    int
-	hasPackDigest int
-	hasPurl       int
-	hasCPE        int
+	totalPackages  int
+	hasLicense     int
+	hasPackVersion int
+	hasPackDigest  int
+	hasPurl        int
+	hasCPE         int
 }
 
 func (r *CycloneDXReport) Report() string {
 	var sb strings.Builder
 	sb.WriteString(fmt.Sprintf("%d total packages\n", r.totalPackages))
 
+	sb.WriteString(fmt.Sprintf("%d%% have versions.\n", scorecard.PrettyPercent(r.hasPackVersion, r.totalPackages)))
 	sb.WriteString(fmt.Sprintf("%d%% have licenses.\n", scorecard.PrettyPercent(r.hasLicense, r.totalPackages)))
 	sb.WriteString(fmt.Sprintf("%d%% have package digest.\n", scorecard.PrettyPercent(r.hasPackDigest, r.totalPackages)))
 	sb.WriteString(fmt.Sprintf("%d%% have purls.\n", scorecard.PrettyPercent(r.hasPurl, r.totalPackages)))
@@ -65,6 +67,12 @@ func (r *CycloneDXReport) PackageIdentification() scorecard.ReportValue {
 }
 
 func (r *CycloneDXReport) PackageVersions() scorecard.ReportValue {
+	return scorecard.ReportValue{
+		Ratio: float32(r.hasPackVersion) / float32(r.totalPackages),
+	}
+}
+
+func (r *CycloneDXReport) PackageDigests() scorecard.ReportValue {
 	return scorecard.ReportValue{
 		Ratio: float32(r.hasPackDigest) / float32(r.totalPackages),
 	}
@@ -128,6 +136,9 @@ func GetCycloneDXReport(filename string) scorecard.SbomReport {
 			if p.Hashes != nil && len(*p.Hashes) > 0 {
 				r.hasPackDigest += 1
 			}
+			if p.Version != "" {
+				r.hasPackVersion += 1
+			}
 			if p.PackageURL != "" {
 				r.hasPurl += 1
 			}


Test patch:
diff --git a/pkg/cdx/cdx_report_test.go b/pkg/cdx/cdx_report_test.go
index 2dc9861..c7fe4b8 100644
--- a/pkg/cdx/cdx_report_test.go
+++ b/pkg/cdx/cdx_report_test.go
@@ -22,6 +22,7 @@ func TestCycloneE2eReport(t *testing.T) {
 	assertTextEqual(t,
 		report_text,
 		`167 total packages
+100% have versions.
 79% have licenses.
 100% have package digest.
 100% have purls.