diff --git a/server/vulnerabilities/nvd/cpe_matching_rules.go b/server/vulnerabilities/nvd/cpe_matching_rules.go index 01fa401bb2..2eca77e570 100644 --- a/server/vulnerabilities/nvd/cpe_matching_rules.go +++ b/server/vulnerabilities/nvd/cpe_matching_rules.go @@ -209,6 +209,15 @@ func GetKnownNVDBugRules() (CPEMatchingRules, error) { return cpeMeta.Product == "visual_studio_code" && cpeMeta.TargetSW == wfn.Any }, }, + // Old macos CPEs without version constraints that should be ignored + CPEMatchingRule{ + CVEs: map[string]struct{}{ + "CVE-2001-0102": {}, + "CVE-1999-0590": {}, + "CVE-1999-0524": {}, + }, + IgnoreAll: true, + }, } for i, rule := range rules { diff --git a/server/vulnerabilities/nvd/cve_test.go b/server/vulnerabilities/nvd/cve_test.go index 924c6b7a10..0197928ae9 100644 --- a/server/vulnerabilities/nvd/cve_test.go +++ b/server/vulnerabilities/nvd/cve_test.go @@ -312,6 +312,21 @@ func TestTranslateCPEToCVE(t *testing.T) { }, continuesToUpdate: true, }, + "cpe:2.3:a:apple:safari:17.0:*:*:*:*:macos:*:*": { + includedCVEs: []cve{ + {ID: "CVE-2023-42852", resolvedInVersion: "17.1"}, + {ID: "CVE-2023-42950", resolvedInVersion: "17.2"}, + {ID: "CVE-2024-23273", resolvedInVersion: "17.4"}, + }, + excludedCVEs: []string{"CVE-2023-28205"}, + continuesToUpdate: true, + }, + "cpe:2.3:a:apple:safari:16.4.0:*:*:*:*:macos:*:*": { + includedCVEs: []cve{ + {ID: "CVE-2023-28205", resolvedInVersion: "16.4.1"}, + }, + continuesToUpdate: true, + }, } cveOSTests := []struct { @@ -474,7 +489,9 @@ func TestTranslateCPEToCVE(t *testing.T) { } for _, cve := range tc.excludedCVEs { - require.NotContains(t, cvesFound[cpe], cve, tc.cpe) + for _, cveFound := range cvesFound[cpe] { + require.NotEqual(t, cve, cveFound.ID, fmt.Sprintf("%s should not contain %s", cpe, cve)) + } } } diff --git a/server/vulnerabilities/nvd/tools/cvefeed/nvd/match_cpe.go b/server/vulnerabilities/nvd/tools/cvefeed/nvd/match_cpe.go index f82866a3dc..ce0d271844 100644 --- a/server/vulnerabilities/nvd/tools/cvefeed/nvd/match_cpe.go +++ b/server/vulnerabilities/nvd/tools/cvefeed/nvd/match_cpe.go @@ -69,13 +69,42 @@ func (cm *cpeMatch) Match(attrs []*wfn.Attributes, requireVersion bool) (matches if cm.match(attr, requireVersion) { matches = append(matches, attr) } - if osMatch := cm.MatchTargetSW(attr); osMatch != nil { + if osMatch := cm.matchTargetSW(attr); osMatch != nil { matches = append(matches, osMatch) } } return matches } +// matchTargetSW returns an OS CPE for the given application CPE +// if the application CPE has a target software attribute and +// matches the given cpeMatch +func (cm *cpeMatch) matchTargetSW(attr *wfn.Attributes) *wfn.Attributes { + if cm == nil || attr == nil { + return nil + } + + if attr.Part != "a" || attr.TargetSW == "" { + return nil + } + + osAttr := &wfn.Attributes{ + Part: "o", + Product: attr.TargetSW, + } + + partMatches := cm.Part == osAttr.Part + productMatches := cm.Product == osAttr.Product + versionMatches := cm.Version == wfn.NA || cm.Version == wfn.Any + noVersionRanges := !cm.hasVersionRanges + + if partMatches && productMatches && versionMatches && noVersionRanges { + return osAttr + } + + return nil +} + // Match implements wfn.Matcher interface func (cm *cpeMatch) match(attr *wfn.Attributes, requireVersion bool) bool { if cm == nil || cm.Attributes == nil { diff --git a/server/vulnerabilities/nvd/tools/wfn/matcher.go b/server/vulnerabilities/nvd/tools/wfn/matcher.go index 8278a371c5..ddde1bed13 100644 --- a/server/vulnerabilities/nvd/tools/wfn/matcher.go +++ b/server/vulnerabilities/nvd/tools/wfn/matcher.go @@ -51,29 +51,6 @@ func (a *Attributes) MatchWithoutVersion(attr *Attributes) bool { matchAttr(a.Other, attr.Other) } -func (a *Attributes) MatchTargetSW(attr *Attributes) *Attributes { - if a == nil || attr == nil { - return nil - } - - var osMatch bool - var osAttr *Attributes - if attr.Part == "a" && attr.TargetSW != "" { - osAttr = &Attributes{ - Part: "o", - Product: attr.TargetSW, - } - - osMatch = matchAttr(a.Part, osAttr.Part) && matchAttr(a.Product, osAttr.Product) - } - - if !osMatch { - return nil - } - - return osAttr -} - // MatchAll returns a Matcher which matches only if all matchers match func MatchAll(ms ...Matcher) Matcher { return &multiMatcher{matchers: ms, allMatch: true}