diff --git a/internal/scan/frameworks/cve_internal_test.go b/internal/scan/frameworks/cve_internal_test.go index a36a0df..e8d58f3 100644 --- a/internal/scan/frameworks/cve_internal_test.go +++ b/internal/scan/frameworks/cve_internal_test.go @@ -65,6 +65,18 @@ func TestResolveVersionFeedsCVELookup(t *testing.T) { } } +// Drupal's generator meta only exposes a bare major, so the extracted version +// is "10"/"9". the cve lookup must still surface the matching CVE instead of +// missing it because the affected entries are dotted. +func TestGetVulnerabilitiesBareMajor(t *testing.T) { + if cves, _ := getVulnerabilities("Drupal", "10"); len(cves) == 0 { + t.Error("expected Drupal 10 to surface CVE-2023-44487, got none") + } + if cves, _ := getVulnerabilities("Drupal", "9"); len(cves) == 0 { + t.Error("expected Drupal 9 to surface CVE-2023-44487, got none") + } +} + func TestVersionAffected(t *testing.T) { tests := []struct { version string @@ -77,6 +89,13 @@ func TestVersionAffected(t *testing.T) { {"4.20", "4.2", false}, // the boundary bug: 4.20 is not a 4.2.x release {"4.20.0", "4.2", false}, {"5.0", "4.2", false}, + // a coarser version covers the listed sub-versions: Drupal's generator + // only emits a bare major, which must still match its dotted entries. + {"10", "10.0", true}, + {"9", "9.3", true}, + // coarser matching stays on dotted boundaries: "1" is not "10.x". + {"1", "10.0", false}, + {"10", "100.0", false}, } for _, tt := range tests { diff --git a/internal/scan/frameworks/detect.go b/internal/scan/frameworks/detect.go index bf2dd2a..e57277d 100644 --- a/internal/scan/frameworks/detect.go +++ b/internal/scan/frameworks/detect.go @@ -219,8 +219,13 @@ func getVulnerabilities(framework, version string) ([]string, []string) { } // versionAffected reports whether version falls under an affected-version -// entry. the entry is a version prefix, matched only on dotted boundaries, so -// "4.2" covers 4.2 and 4.2.1 but not 4.20. +// entry. matching is on dotted component boundaries in either direction, so +// "4.2" covers 4.2 and 4.2.1 but not 4.20, and a coarser version covers the +// listed sub-versions. the latter matters because some detectors only recover +// a bare major (Drupal's generator emits "10"); without it a bare major never +// matches a dotted affected entry and the CVE is silently missed. func versionAffected(version, affected string) bool { - return version == affected || strings.HasPrefix(version, affected+".") + return version == affected || + strings.HasPrefix(version, affected+".") || + strings.HasPrefix(affected, version+".") }