App OS Vulnerability Matching (#19486)

This commit is contained in:
Tim Lee 2024-06-13 11:20:28 -04:00 committed by GitHub
parent 42b1fc7e30
commit 0516cd61d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 366 additions and 4 deletions

View 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)

View file

@ -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 {

View file

@ -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

View file

@ -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
}

View file

@ -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
}