diff --git a/server/vulnerabilities/oval/input/ubuntu_result.go b/server/vulnerabilities/oval/input/ubuntu_result.go
index e503fba3cc..d59a534a53 100644
--- a/server/vulnerabilities/oval/input/ubuntu_result.go
+++ b/server/vulnerabilities/oval/input/ubuntu_result.go
@@ -7,5 +7,10 @@ type UbuntuResultXML struct {
DpkgInfoTests []DpkgInfoTestXML
DpkgInfoStates []DpkgInfoStateXML
DpkgInfoObjects []PackageInfoTestObjectXML
+ UnameTests []UnixUnameTestXML
+ UnameStates []UnixUnameStateXML
+ VariableTests []VariableTestXML
+ VariableObjects map[int]VariableObjectXML
+ VariableStates []VariableStateXML
Variables map[string]ConstantVariableXML
}
diff --git a/server/vulnerabilities/oval/input/unix_uname_state.go b/server/vulnerabilities/oval/input/unix_uname_state.go
new file mode 100644
index 0000000000..afbf680abe
--- /dev/null
+++ b/server/vulnerabilities/oval/input/unix_uname_state.go
@@ -0,0 +1,7 @@
+package oval_input
+
+// UnixUnameStateXML see https://oval.mitre.org/language/version5.10.1/ovaldefinition/documentation/unix-definitions-schema.html#uname_state
+type UnixUnameStateXML struct {
+ Id string `xml:"id,attr"`
+ OSRelease *SimpleTypeXML `xml:"os_release"`
+}
diff --git a/server/vulnerabilities/oval/input/unix_uname_test_object.go b/server/vulnerabilities/oval/input/unix_uname_test_object.go
new file mode 100644
index 0000000000..0c8330254f
--- /dev/null
+++ b/server/vulnerabilities/oval/input/unix_uname_test_object.go
@@ -0,0 +1,11 @@
+package oval_input
+
+type unixUnameTestStateXML struct {
+ Id string `xml:"state_ref,attr"`
+}
+
+// https://oval.mitre.org/language/version5.10.1/ovaldefinition/documentation/unix-definitions-schema.html#uname_state
+type UnixUnameTestXML struct {
+ Id string `xml:"id,attr"`
+ States []unixUnameTestStateXML `xml:"state"`
+}
diff --git a/server/vulnerabilities/oval/input/variable_object.go b/server/vulnerabilities/oval/input/variable_object.go
new file mode 100644
index 0000000000..b5a098e252
--- /dev/null
+++ b/server/vulnerabilities/oval/input/variable_object.go
@@ -0,0 +1,7 @@
+package oval_input
+
+// https://oval.mitre.org/language/version5.10.1/ovaldefinition/documentation/independent-definitions-schema.html#variable_object
+type VariableObjectXML struct {
+ Id string `xml:"id,attr"`
+ RefID string `xml:"var_ref"`
+}
diff --git a/server/vulnerabilities/oval/input/variable_state.go b/server/vulnerabilities/oval/input/variable_state.go
new file mode 100644
index 0000000000..c8c6533c6e
--- /dev/null
+++ b/server/vulnerabilities/oval/input/variable_state.go
@@ -0,0 +1,7 @@
+package oval_input
+
+// https://oval.mitre.org/language/version5.10.1/ovaldefinition/documentation/independent-definitions-schema.html#variable_state
+type VariableStateXML struct {
+ Id string `xml:"id,attr"`
+ Value SimpleTypeXML `xml:"value"`
+}
diff --git a/server/vulnerabilities/oval/input/variable_test_object.go b/server/vulnerabilities/oval/input/variable_test_object.go
new file mode 100644
index 0000000000..c46434fde6
--- /dev/null
+++ b/server/vulnerabilities/oval/input/variable_test_object.go
@@ -0,0 +1,16 @@
+package oval_input
+
+type variableObjectXML struct {
+ Id string `xml:"object_ref"`
+}
+
+type variableStateXML struct {
+ Id string `xml:"state_ref,attr"`
+}
+
+// https://oval.mitre.org/language/version5.10.1/ovaldefinition/documentation/independent-definitions-schema.html#variable_test
+type VariableTestXML struct {
+ Id string `xml:"id,attr"`
+ Object variableObjectXML `xml:"object"`
+ States []variableStateXML `xml:"state"`
+}
diff --git a/server/vulnerabilities/oval/mappers.go b/server/vulnerabilities/oval/mappers.go
index e39f461e85..f7f9e57750 100644
--- a/server/vulnerabilities/oval/mappers.go
+++ b/server/vulnerabilities/oval/mappers.go
@@ -277,6 +277,17 @@ func mapDpkgInfoTest(i oval_input.DpkgInfoTestXML) (int, *oval_parsed.DpkgInfoTe
return id, &tst, nil
}
+func mapUnixUnameTest(i oval_input.UnixUnameTestXML) (int, *oval_parsed.UnixUnameTest, error) {
+ id, err := extractId(i.Id)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ tst := oval_parsed.UnixUnameTest{}
+
+ return id, &tst, nil
+}
+
// mapDpkgInfoState maps a DpkgInfoStateXML into an EVR string. The state of an object defines
// the different information that can be used to evaluate the specified DPKG package. All Ubuntu
// OVAL definitions seem to only use Evr strings to define object state, that's why only Evr support
@@ -296,3 +307,24 @@ func mapDpkgInfoState(sta oval_input.DpkgInfoStateXML) (*oval_parsed.ObjectState
r := oval_parsed.NewObjectStateEvrString(sta.Evr.Op, sta.Evr.Value)
return &r, nil
}
+
+func mapUnameState(sta oval_input.UnixUnameStateXML) *oval_parsed.ObjectStateString {
+ r := oval_parsed.NewObjectStateString(sta.OSRelease.Op, sta.OSRelease.Value)
+ return &r
+}
+
+func mapVariableTest(i oval_input.VariableTestXML) (int, *oval_parsed.UnixUnameTest, error) {
+ id, err := extractId(i.Id)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ tst := oval_parsed.UnixUnameTest{}
+
+ return id, &tst, nil
+}
+
+func mapVariableState(sta oval_input.VariableStateXML) *oval_parsed.ObjectStateString {
+ r := oval_parsed.NewObjectStateString(sta.Value.Op, sta.Value.Value)
+ return &r
+}
diff --git a/server/vulnerabilities/oval/parsed/ind_variabletest.go b/server/vulnerabilities/oval/parsed/ind_variabletest.go
new file mode 100644
index 0000000000..b7b2b7f306
--- /dev/null
+++ b/server/vulnerabilities/oval/parsed/ind_variabletest.go
@@ -0,0 +1,9 @@
+package oval_parsed
+
+type VariableTest struct {
+ Objects []string
+ States []ObjectStateEvrString
+ StateOperator OperatorType
+ ObjectMatch ObjectMatchType
+ StateMatch StateMatchType
+}
diff --git a/server/vulnerabilities/oval/parsed/ubuntu_result.go b/server/vulnerabilities/oval/parsed/ubuntu_result.go
index e777a91833..b7169dc7b0 100644
--- a/server/vulnerabilities/oval/parsed/ubuntu_result.go
+++ b/server/vulnerabilities/oval/parsed/ubuntu_result.go
@@ -7,6 +7,7 @@ import (
type UbuntuResult struct {
Definitions []Definition
PackageTests map[int]*DpkgInfoTest
+ UnameTests map[int]*UnixUnameTest
}
// NewUbuntuResult is the result of parsing an OVAL file that targets an Ubuntu distro.
@@ -14,6 +15,7 @@ type UbuntuResult struct {
func NewUbuntuResult() *UbuntuResult {
return &UbuntuResult{
PackageTests: make(map[int]*DpkgInfoTest),
+ UnameTests: make(map[int]*UnixUnameTest),
}
}
@@ -27,6 +29,10 @@ func (r *UbuntuResult) AddPackageTest(id int, tst *DpkgInfoTest) {
r.PackageTests[id] = tst
}
+func (r *UbuntuResult) AddUnameTest(id int, tst *UnixUnameTest) {
+ r.UnameTests[id] = tst
+}
+
func (r UbuntuResult) Eval(ver fleet.OSVersion, software []fleet.Software) ([]fleet.SoftwareVulnerability, error) {
// Test Id => Matching software
pkgTstResults := make(map[int][]fleet.Software)
diff --git a/server/vulnerabilities/oval/parsed/unix_unameState.go b/server/vulnerabilities/oval/parsed/unix_unameState.go
new file mode 100644
index 0000000000..f3f9ae0083
--- /dev/null
+++ b/server/vulnerabilities/oval/parsed/unix_unameState.go
@@ -0,0 +1,6 @@
+package oval_parsed
+
+type UnixUnameState struct {
+ States []ObjectStateString
+ StateMatch StateMatchType
+}
diff --git a/server/vulnerabilities/oval/parsed/unix_unameTest.go b/server/vulnerabilities/oval/parsed/unix_unameTest.go
new file mode 100644
index 0000000000..3a7d358466
--- /dev/null
+++ b/server/vulnerabilities/oval/parsed/unix_unameTest.go
@@ -0,0 +1,5 @@
+package oval_parsed
+
+type UnixUnameTest struct {
+ States []ObjectStateString
+}
diff --git a/server/vulnerabilities/oval/parser.go b/server/vulnerabilities/oval/parser.go
index 62e9e8f6af..5f1b937bb8 100644
--- a/server/vulnerabilities/oval/parser.go
+++ b/server/vulnerabilities/oval/parser.go
@@ -254,17 +254,17 @@ func mapToRhelResult(xmlResult *oval_input.RhelResultXML) (*oval_parsed.RhelResu
func processUbuntuDef(r io.Reader) ([]byte, error) {
xmlResult, err := parseUbuntuXML(r)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("parsing ubuntu xml: %w", err)
}
result, err := mapToUbuntuResult(xmlResult)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("mapping ubuntu result: %w", err)
}
payload, err := json.Marshal(result)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("marshalling ubuntu result: %w", err)
}
return payload, nil
@@ -272,7 +272,8 @@ func processUbuntuDef(r io.Reader) ([]byte, error) {
func parseUbuntuXML(reader io.Reader) (*oval_input.UbuntuResultXML, error) {
r := &oval_input.UbuntuResultXML{
- Variables: make(map[string]oval_input.ConstantVariableXML),
+ Variables: make(map[string]oval_input.ConstantVariableXML),
+ VariableObjects: make(map[int]oval_input.VariableObjectXML),
}
d := xml.NewDecoder(reader)
@@ -290,35 +291,76 @@ func parseUbuntuXML(reader io.Reader) (*oval_input.UbuntuResultXML, error) {
if t.Name.Local == "definition" {
def := oval_input.DefinitionXML{}
if err = d.DecodeElement(&def, &t); err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding definition: %w", err)
}
r.Definitions = append(r.Definitions, def)
}
if t.Name.Local == "dpkginfo_test" {
tst := oval_input.DpkgInfoTestXML{}
if err = d.DecodeElement(&tst, &t); err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding dpkginfo_test: %w", err)
}
r.DpkgInfoTests = append(r.DpkgInfoTests, tst)
}
if t.Name.Local == "dpkginfo_state" {
sta := oval_input.DpkgInfoStateXML{}
if err = d.DecodeElement(&sta, &t); err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding dpkginfo_state: %w", err)
}
r.DpkgInfoStates = append(r.DpkgInfoStates, sta)
}
if t.Name.Local == "dpkginfo_object" {
obj := oval_input.PackageInfoTestObjectXML{}
if err = d.DecodeElement(&obj, &t); err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding dpkginfo_object: %w", err)
}
r.DpkgInfoObjects = append(r.DpkgInfoObjects, obj)
}
+ if t.Name.Local == "uname_test" {
+ tst := oval_input.UnixUnameTestXML{}
+ if err = d.DecodeElement(&tst, &t); err != nil {
+ return nil, fmt.Errorf("decoding uname_test: %w", err)
+ }
+ r.UnameTests = append(r.UnameTests, tst)
+ }
+ if t.Name.Local == "uname_state" {
+ sta := oval_input.UnixUnameStateXML{}
+ if err = d.DecodeElement(&sta, &t); err != nil {
+ return nil, fmt.Errorf("decoding uname_state: %w", err)
+ }
+ r.UnameStates = append(r.UnameStates, sta)
+ }
+ if t.Name.Local == "variable_test" {
+ tst := oval_input.VariableTestXML{}
+ if err = d.DecodeElement(&tst, &t); err != nil {
+ return nil, fmt.Errorf("decoding variable_test: %w", err)
+ }
+ r.VariableTests = append(r.VariableTests, tst)
+ }
+ if t.Name.Local == "variable_object" {
+ obj := oval_input.VariableObjectXML{}
+ if err = d.DecodeElement(&obj, &t); err != nil {
+ return nil, fmt.Errorf("decoding variable_object: %w", err)
+ }
+
+ id, err := extractId(obj.Id)
+ if err != nil {
+ return nil, fmt.Errorf("extracting id: %w", err)
+ }
+ r.VariableObjects[id] = obj
+ }
+
+ if t.Name.Local == "variable_state" {
+ sta := oval_input.VariableStateXML{}
+ if err = d.DecodeElement(&sta, &t); err != nil {
+ return nil, fmt.Errorf("decoding variable_state: %w", err)
+ }
+ r.VariableStates = append(r.VariableStates, sta)
+ }
if t.Name.Local == "constant_variable" {
cVar := oval_input.ConstantVariableXML{}
if err = d.DecodeElement(&cVar, &t); err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding constant_variable: %w", err)
}
r.Variables[cVar.Id] = cVar
}
@@ -331,12 +373,14 @@ func mapToUbuntuResult(xmlResult *oval_input.UbuntuResultXML) (*oval_parsed.Ubun
staToTst := make(map[string][]int)
objToTst := make(map[string][]int)
+ ustaToTst := make(map[string][]int)
+ vstaToTst := make(map[string][]int)
for _, d := range xmlResult.Definitions {
if len(d.Vulnerabilities) > 0 {
def, err := mapDefinition(d)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("mapping definition: %w", err)
}
r.AddDefinition(*def)
}
@@ -345,7 +389,7 @@ func mapToUbuntuResult(xmlResult *oval_input.UbuntuResultXML) (*oval_parsed.Ubun
for _, t := range xmlResult.DpkgInfoTests {
id, tst, err := mapDpkgInfoTest(t)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("mapping dpkg info test: %w", err)
}
objToTst[t.Object.Id] = append(objToTst[t.Object.Id], id)
@@ -358,7 +402,7 @@ func mapToUbuntuResult(xmlResult *oval_input.UbuntuResultXML) (*oval_parsed.Ubun
for _, o := range xmlResult.DpkgInfoObjects {
obj, err := mapPackageInfoTestObject(o, xmlResult.Variables)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("mapping dpkg info object: %w", err)
}
for _, tId := range objToTst[o.Id] {
@@ -374,7 +418,7 @@ func mapToUbuntuResult(xmlResult *oval_input.UbuntuResultXML) (*oval_parsed.Ubun
for _, s := range xmlResult.DpkgInfoStates {
sta, err := mapDpkgInfoState(s)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("mapping dpkg info state: %w", err)
}
for _, tId := range staToTst[s.Id] {
t, ok := r.PackageTests[tId]
@@ -385,5 +429,66 @@ func mapToUbuntuResult(xmlResult *oval_input.UbuntuResultXML) (*oval_parsed.Ubun
}
}
}
+
+ for _, t := range xmlResult.UnameTests {
+ id, tst, err := mapUnixUnameTest(t)
+ if err != nil {
+ return nil, fmt.Errorf("mapping uname test: %w", err)
+ }
+
+ for _, sta := range t.States {
+ ustaToTst[sta.Id] = append(ustaToTst[sta.Id], id)
+ }
+ r.AddUnameTest(id, tst)
+ }
+
+ for _, s := range xmlResult.UnameStates {
+ sta := mapUnameState(s)
+ for _, tId := range ustaToTst[s.Id] {
+ t, ok := r.UnameTests[tId]
+ if ok {
+ t.States = append(t.States, *sta)
+ } else {
+ return nil, fmt.Errorf("test not found: %d", tId)
+ }
+ }
+ }
+
+ for _, t := range xmlResult.VariableTests {
+ id, tst, err := mapVariableTest(t)
+ if err != nil {
+ return nil, fmt.Errorf("mapping variable test: %w", err)
+ }
+
+ // Skip tests that are not used in any uname test
+ // (there should be none)
+ if obj, ok := xmlResult.VariableObjects[id]; ok {
+ id, err := extractId(obj.RefID)
+ if err != nil {
+ return nil, fmt.Errorf("extracting variable object id: %w", err)
+ }
+ if _, ok := r.UnameTests[id]; !ok {
+ continue
+ }
+ }
+
+ for _, sta := range t.States {
+ vstaToTst[sta.Id] = append(vstaToTst[sta.Id], id)
+ }
+ r.AddUnameTest(id, tst)
+ }
+
+ for _, s := range xmlResult.VariableStates {
+ sta := mapVariableState(s)
+ for _, tId := range vstaToTst[s.Id] {
+ t, ok := r.UnameTests[tId]
+ if ok {
+ t.States = append(t.States, *sta)
+ } else {
+ return nil, fmt.Errorf("test not found: %d", tId)
+ }
+ }
+ }
+
return r, nil
}
diff --git a/server/vulnerabilities/oval/parser_test.go b/server/vulnerabilities/oval/parser_test.go
index 79c9e3ec57..ee3597752a 100644
--- a/server/vulnerabilities/oval/parser_test.go
+++ b/server/vulnerabilities/oval/parser_test.go
@@ -59,6 +59,38 @@ func TestOvalParser(t *testing.T) {
+
+
+ USN-5544-1 -- Linux kernel vulnerabilities
+
+ Ubuntu 22.04 LTS
+
+
+
+
+
+
+ Some long description
+
+ High
+
+ CVE-2022-1652
+ CVE-2022-1679
+ CVE-2022-28893
+ CVE-2022-34918
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -68,7 +100,15 @@ func TestOvalParser(t *testing.T) {
+
+
+
+
+
+
+
+
@@ -76,6 +116,10 @@ func TestOvalParser(t *testing.T) {
+
+
+ oval:com.ubuntu.jammy:var:554410000000
+
@@ -84,6 +128,12 @@ func TestOvalParser(t *testing.T) {
1:9.18.1-1ubuntu1.1
+
+ 5.15.0-\d+(-generic|-generic-64k|-generic-lpae|-lowlatency|-lowlatency-64k)
+
+
+ 0:5.15.0-43
+
@@ -351,14 +401,8 @@ func TestOvalParser(t *testing.T) {
require.NoError(t, err)
var expectedVulns []string
- var expectedTestIds []int
for _, d := range xmlResult.Definitions {
- for _, c := range d.Criteria.Criteriums {
- tstId, err := extractId(c.TestId)
- require.NoError(t, err)
- expectedTestIds = append(expectedTestIds, tstId)
- }
for _, v := range d.Vulnerabilities {
expectedVulns = append(expectedVulns, v.Id)
}
@@ -373,6 +417,8 @@ func TestOvalParser(t *testing.T) {
}
require.Equal(t, expectedVulns, actualVulns)
+
+ expectedTestIds := []int{540210000000, 542910000000, 554410000000, 554410000010}
require.ElementsMatch(t, expectedTestIds, actualTestIds)
require.Len(t, result.PackageTests, 2)
@@ -399,6 +445,13 @@ func TestOvalParser(t *testing.T) {
"bind9-dnsutils",
"bind9-host",
})
+
+ require.Len(t, result.UnameTests, 2)
+ matchState := []oval_parsed.ObjectStateString{"pattern match|5.15.0-\\d+(-generic|-generic-64k|-generic-lpae|-lowlatency|-lowlatency-64k)"}
+ require.ElementsMatch(t, result.UnameTests[554410000000].States, matchState)
+
+ variableState := []oval_parsed.ObjectStateString{"less than|0:5.15.0-43"}
+ require.ElementsMatch(t, result.UnameTests[554410000010].States, variableState)
})
t.Run("#parseRhelXML", func(t *testing.T) {