mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
App OS Vulnerability Matching (#19486)
This commit is contained in:
parent
42b1fc7e30
commit
0516cd61d0
5 changed files with 366 additions and 4 deletions
1
changes/148940-app-os-vuln-matching
Normal file
1
changes/148940-app-os-vuln-matching
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Fleet now matches vulnerabilies for applications that include an OS scope [example](https://nvd.nist.gov/vuln/detail/CVE-2023-0400)
|
||||
|
|
@ -307,6 +307,12 @@ func TestTranslateCPEToCVE(t *testing.T) {
|
|||
},
|
||||
continuesToUpdate: false,
|
||||
},
|
||||
"cpe:2.3:a:adobe:animate:*:*:*:*:*:macos:*:*": {
|
||||
includedCVEs: []cve{
|
||||
{ID: "CVE-2023-44325"},
|
||||
},
|
||||
continuesToUpdate: true,
|
||||
},
|
||||
}
|
||||
|
||||
cveOSTests := []struct {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,15 @@ func TestMatchJSON(t *testing.T) {
|
|||
{Part: "a", Vendor: "mozilla", Product: "firefox", Version: "64\\.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Rule: 3,
|
||||
Inventory: []*wfn.Attributes{
|
||||
{Part: "o", Vendor: "apple", Product: "macos", Version: "14\\.1\\.2"},
|
||||
},
|
||||
Matches: []*wfn.Attributes{
|
||||
{Part: "o", Vendor: "apple", Product: "macos", Version: "14\\.1\\.2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
items, err := ParseJSON(bytes.NewBufferString(testJSONdict))
|
||||
if err != nil {
|
||||
|
|
@ -103,7 +112,7 @@ func TestMatchJSON(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
mm := items[c.Rule].Match(c.Inventory, false)
|
||||
if len(mm) != len(c.Matches) {
|
||||
t.Fatalf("expected %d matches, got %d matches", len(mm), len(c.Matches))
|
||||
t.Fatalf("expected %d matches, got %d matches", len(c.Matches), len(mm))
|
||||
}
|
||||
if len(mm) > 0 && !matchesAll(mm, c.Matches) {
|
||||
t.Fatalf("wrong match: expected %v, got %v", c.Matches, mm)
|
||||
|
|
@ -112,6 +121,45 @@ func TestMatchJSON(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTargetSWMatching(t *testing.T) {
|
||||
inventoryAcrobat := []*wfn.Attributes{
|
||||
{Part: "a", Vendor: "adobe", Product: "acrobat", Version: "20\\.001\\.3005", TargetSW: "macos"},
|
||||
}
|
||||
|
||||
items, err := ParseJSON(bytes.NewBufferString(targetSWMatchingJSON))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse the dictionary: %v", err)
|
||||
}
|
||||
// matches OS on targetSW
|
||||
if mm := items[0].Match(inventoryAcrobat, true); len(mm) == 0 {
|
||||
t.Fatal("expected Match to match, it did not")
|
||||
}
|
||||
|
||||
// does not match OS on targetSW
|
||||
if mm := items[1].Match(inventoryAcrobat, true); len(mm) != 0 {
|
||||
t.Fatal("expected Match to not match, it did")
|
||||
}
|
||||
|
||||
// matches when OS is not present
|
||||
if mm := items[2].Match(inventoryAcrobat, true); len(mm) == 0 {
|
||||
t.Fatal("expected Match to match, it did not")
|
||||
}
|
||||
|
||||
// does not match OS on targetSW with multiple nodes
|
||||
if mm := items[3].Match(inventoryAcrobat, true); len(mm) != 0 {
|
||||
t.Fatal("expected Match to not match, it did")
|
||||
}
|
||||
|
||||
inventoryWrongOS := []*wfn.Attributes{
|
||||
{Part: "a", Vendor: "adobe", Product: "acrobat", Version: "20\\.001\\.3005", TargetSW: "linux"},
|
||||
}
|
||||
|
||||
// does not match OS on targetSW
|
||||
if mm := items[0].Match(inventoryWrongOS, true); len(mm) != 0 {
|
||||
t.Fatal("expected Match to not match, it did")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchJSONrequireVersion(t *testing.T) {
|
||||
inventory := []*wfn.Attributes{
|
||||
{Part: "a", Vendor: "microsoft", Product: "ie", Version: "6\\.0"},
|
||||
|
|
@ -279,9 +327,265 @@ var testJSONdict = `{
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cve": {
|
||||
"affects": null,
|
||||
"CVE_data_meta": {
|
||||
"ASSIGNER": "product-security@apple.com",
|
||||
"ID": "CVE-2023-42919"
|
||||
},
|
||||
"data_format": "MITRE",
|
||||
"data_type": "CVE",
|
||||
"data_version": "4.0"
|
||||
},
|
||||
"configurations": {
|
||||
"CVE_data_version": "4.0",
|
||||
"nodes": [
|
||||
{
|
||||
"cpe_match": [
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:ipados:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "16.7.3",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:ipados:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "17.2",
|
||||
"versionStartIncluding": "17.0",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:iphone_os:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "16.7.3",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:iphone_os:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "17.2",
|
||||
"versionStartIncluding": "17.0",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "12.7.2",
|
||||
"versionStartIncluding": "12.0.0",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "13.6.3",
|
||||
"versionStartIncluding": "13.0",
|
||||
"vulnerable": true
|
||||
},
|
||||
{
|
||||
"cpe23Uri": "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "14.2",
|
||||
"versionStartIncluding": "14.0",
|
||||
"vulnerable": true
|
||||
}
|
||||
],
|
||||
"operator": "OR"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
] }`
|
||||
|
||||
var targetSWMatchingJSON = `{
|
||||
"CVE_data_type" : "CVE",
|
||||
"CVE_data_format" : "MITRE",
|
||||
"CVE_data_version" : "4.0",
|
||||
"CVE_data_numberOfCVEs" : "7083",
|
||||
"CVE_data_timestamp" : "2018-07-31T07:00Z",
|
||||
"CVE_Items" : [ {
|
||||
"cve" : {
|
||||
"data_type" : "CVE",
|
||||
"data_format" : "MITRE",
|
||||
"data_version" : "4.0",
|
||||
"CVE_data_meta" : {
|
||||
"ID" : "CVE-2023-26369",
|
||||
"ASSIGNER" : "psirt@adobe.com"
|
||||
}
|
||||
},
|
||||
"configurations" : {
|
||||
"CVE_data_version" : "4.0",
|
||||
"nodes" : [ {
|
||||
"operator" : "AND",
|
||||
"children" : [ {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat:*:*:*:*:classic:*:*:*",
|
||||
"versionStartIncluding" : "20.001.3005",
|
||||
"versionEndExcluding" : "20.005.30524",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_dc:*:*:*:*:continuous:*:*:*",
|
||||
"versionStartIncluding" : "15.007.20033",
|
||||
"versionEndExcluding" : "23.006.20320",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_reader:*:*:*:*:classic:*:*:*",
|
||||
"versionStartIncluding" : "20.001.3005",
|
||||
"versionEndExcluding" : "20.005.30524",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_reader_dc:*:*:*:*:continuous:*:*:*",
|
||||
"versionStartIncluding" : "15.007.20033",
|
||||
"versionEndExcluding" : "23.006.20320",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : false,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:-:*:*:*:*:*:*:*",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : false,
|
||||
"cpe23Uri" : "cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:*",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
} ],
|
||||
"cpe_match" : [ ]
|
||||
} ]
|
||||
}
|
||||
}, {
|
||||
"cve" : {
|
||||
"data_type" : "CVE",
|
||||
"data_format" : "MITRE",
|
||||
"data_version" : "4.0",
|
||||
"CVE_data_meta" : {
|
||||
"ID" : "CVE-2023-27928",
|
||||
"ASSIGNER" : "product-security@apple.com"
|
||||
}
|
||||
},
|
||||
"configurations" : {
|
||||
"CVE_data_version" : "4.0",
|
||||
"nodes" : [ {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding" : "13.0",
|
||||
"versionEndExcluding" : "13.3",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:tvos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding" : "16.4",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:watchos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding" : "9.4",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding" : "11.7.5",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}, {
|
||||
"cve" : {
|
||||
"data_type" : "CVE",
|
||||
"data_format" : "MITRE",
|
||||
"data_version" : "4.0",
|
||||
"CVE_data_meta" : {
|
||||
"ID" : "CVE-2023-27928",
|
||||
"ASSIGNER" : "product-security@apple.com"
|
||||
}
|
||||
},
|
||||
"configurations" : {
|
||||
"CVE_data_version" : "4.0",
|
||||
"nodes" : [ {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat:*:*:*:*:classic:*:*:*",
|
||||
"versionStartIncluding" : "20.001.3005",
|
||||
"versionEndExcluding" : "20.005.30524",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_dc:*:*:*:*:continuous:*:*:*",
|
||||
"versionStartIncluding" : "15.007.20033",
|
||||
"versionEndExcluding" : "23.006.20320",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_reader:*:*:*:*:classic:*:*:*",
|
||||
"versionStartIncluding" : "20.001.3005",
|
||||
"versionEndExcluding" : "20.005.30524",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:adobe:acrobat_reader_dc:*:*:*:*:continuous:*:*:*",
|
||||
"versionStartIncluding" : "15.007.20033",
|
||||
"versionEndExcluding" : "23.006.20320",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}, {
|
||||
"cve" : {
|
||||
"data_type" : "CVE",
|
||||
"data_format" : "MITRE",
|
||||
"data_version" : "4.0",
|
||||
"CVE_data_meta" : {
|
||||
"ID" : "CVE-2023-28321",
|
||||
"ASSIGNER" : "support@hackerone.com"
|
||||
}
|
||||
},
|
||||
"configurations" : {
|
||||
"CVE_data_version" : "4.0",
|
||||
"nodes" : [ {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:a:haxx:curl:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding" : "8.1.0",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"operator" : "OR",
|
||||
"children" : [ ],
|
||||
"cpe_match" : [ {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding" : "13.0",
|
||||
"versionEndExcluding" : "13.5",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding" : "12.0",
|
||||
"versionEndExcluding" : "12.6.8",
|
||||
"cpe_name" : [ ]
|
||||
}, {
|
||||
"vulnerable" : true,
|
||||
"cpe23Uri" : "cpe:2.3:o:apple:macos:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding" : "11.0",
|
||||
"versionEndExcluding" : "11.7.9",
|
||||
"cpe_name" : [ ]
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
] }
|
||||
`
|
||||
|
||||
func matchesAll(src, tgt []*wfn.Attributes) bool {
|
||||
if len(src) != len(tgt) {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ 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 {
|
||||
matches = append(matches, osMatch)
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,14 +49,37 @@ 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{ms, true}
|
||||
return &multiMatcher{matchers: ms, allMatch: true}
|
||||
}
|
||||
|
||||
// MatchAll returns a Matcher which matches if any of the matchers match
|
||||
func MatchAny(ms ...Matcher) Matcher {
|
||||
return &multiMatcher{ms, false}
|
||||
return &multiMatcher{matchers: ms, allMatch: false}
|
||||
}
|
||||
|
||||
// DontMatch returns a Matcher which matches if the given matchers doesn't
|
||||
|
|
@ -68,12 +91,23 @@ type multiMatcher struct {
|
|||
matchers []Matcher
|
||||
// if true, match will only return something if all matchers matched at least something
|
||||
allMatch bool
|
||||
depth int
|
||||
}
|
||||
|
||||
// Match is part of the Matcher interface
|
||||
func (mm *multiMatcher) Match(attrs []*Attributes, requireVersion bool) []*Attributes {
|
||||
defer func() {
|
||||
if mm.depth > 0 {
|
||||
mm.depth--
|
||||
}
|
||||
}()
|
||||
|
||||
matched := make(map[*Attributes]bool)
|
||||
for _, matcher := range mm.matchers {
|
||||
// type check matcher against multiMatcher
|
||||
if _, ok := matcher.(*multiMatcher); !ok {
|
||||
mm.depth++
|
||||
}
|
||||
matches := matcher.Match(attrs, requireVersion)
|
||||
if mm.allMatch && len(matches) == 0 {
|
||||
// all matchers need to match at least one attr
|
||||
|
|
@ -88,6 +122,11 @@ func (mm *multiMatcher) Match(attrs []*Attributes, requireVersion bool) []*Attri
|
|||
for m := range matched {
|
||||
matches = append(matches, m)
|
||||
}
|
||||
|
||||
if mm.depth == 0 && len(matches) > 1 && !attributesIncludeApp(matches) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
|
|
@ -118,3 +157,12 @@ func (nm notMatcher) Match(attrs []*Attributes, requireVersion bool) (matches []
|
|||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func attributesIncludeApp(attrs []*Attributes) bool {
|
||||
for _, a := range attrs {
|
||||
if a.Part == "a" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue