From 79bf51b03c9c1c9eb8ed8dc3ff31e76723159ef9 Mon Sep 17 00:00:00 2001 From: Juan Fernandez Date: Fri, 24 Jun 2022 11:02:51 -0400 Subject: [PATCH] Feature 6242: Use oval to detect vulnerabilities on Fedora hosts (#6330) * Feature 6242: Scan Fedora hosts using OVAL definitions --- changes/feature-6242-add-Fedora-oval-support | 1 + cmd/fleet/cron.go | 2 - server/vulnerabilities/oval/oval_platform.go | 4 ++ .../oval/oval_platform_test.go | 27 +++++++++- .../oval/parsed/object_info_state.go | 3 +- server/vulnerabilities/oval/parsed/utils.go | 33 ++++++++++++ server/vulnerabilities/oval/parser_test.go | 51 ++++++++++--------- 7 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 changes/feature-6242-add-Fedora-oval-support create mode 100644 server/vulnerabilities/oval/parsed/utils.go diff --git a/changes/feature-6242-add-Fedora-oval-support b/changes/feature-6242-add-Fedora-oval-support new file mode 100644 index 0000000000..3c8848b0d6 --- /dev/null +++ b/changes/feature-6242-add-Fedora-oval-support @@ -0,0 +1 @@ +- Add support for scanning Fedora hosts using OVAL definitions. diff --git a/cmd/fleet/cron.go b/cmd/fleet/cron.go index 219b69ad3f..50c9653ab3 100644 --- a/cmd/fleet/cron.go +++ b/cmd/fleet/cron.go @@ -240,7 +240,6 @@ func cronVulnerabilities( recentVulns, appConfig, time.Now()); err != nil { - errHandler(ctx, logger, "triggering vulnerabilities webhook", err) } @@ -276,7 +275,6 @@ func cronVulnerabilities( if err := ds.SyncHostsSoftware(ctx, time.Now()); err != nil { errHandler(ctx, logger, "calculating hosts count per software", err) } - level.Debug(logger).Log("loop", "done") } } diff --git a/server/vulnerabilities/oval/oval_platform.go b/server/vulnerabilities/oval/oval_platform.go index ac7b68acab..3d88252949 100644 --- a/server/vulnerabilities/oval/oval_platform.go +++ b/server/vulnerabilities/oval/oval_platform.go @@ -5,6 +5,8 @@ import ( "regexp" "strings" "time" + + oval_parsed "github.com/fleetdm/fleet/v4/server/vulnerabilities/oval/parsed" ) type Platform string @@ -45,6 +47,7 @@ func format(platform string, major string, minor string) string { if platform == "ubuntu" { return fmt.Sprintf("%s_%s%s", platform, major, minor) } + // RHEL based platforms only use the major version for their OVAL definitions return fmt.Sprintf("%s_%s", platform, major) } @@ -55,6 +58,7 @@ func format(platform string, major string, minor string) string { // ('rhel', 'CentOS Linux 7.9.2009') => 'rhel_07'. func NewPlatform(hostPlatform, hostOsVersion string) Platform { nPlatform := strings.Trim(strings.ToLower(hostPlatform), " ") + hostOsVersion = oval_parsed.ReplaceFedoraOSVersion(hostOsVersion) major, minor := getMajorMinorVer(strings.Trim(hostOsVersion, " ")) return Platform(format(nPlatform, major, minor)) } diff --git a/server/vulnerabilities/oval/oval_platform_test.go b/server/vulnerabilities/oval/oval_platform_test.go index 61077d9bf7..73203f43ae 100644 --- a/server/vulnerabilities/oval/oval_platform_test.go +++ b/server/vulnerabilities/oval/oval_platform_test.go @@ -26,10 +26,35 @@ func TestOvalPlatform(t *testing.T) { {"ubuntu", "Ubuntu 18.4.0 ", "ubuntu_1804"}, {"rhel", "CentOS Linux 7.9.2009", "rhel_07"}, {"amzn", "Amazon Linux 2.0.0", "amzn_02"}, + {"rhel", "Fedora Linux 12.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 13.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 14.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 15.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 16.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 17.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 18.0.0", "rhel_06"}, + {"rhel", "Fedora Linux 19.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 20.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 21.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 22.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 23.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 24.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 25.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 26.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 27.0.0", "rhel_07"}, + {"rhel", "Fedora Linux 28.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 29.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 30.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 31.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 32.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 33.0.0", "rhel_08"}, + {"rhel", "Fedora Linux 34.0.0", "rhel_09"}, + {"rhel", "Fedora Linux 35.0.0", "rhel_09"}, + {"rhel", "Fedora Linux 36.0.0", "rhel_09"}, } for _, c := range cases { - require.Equal(t, c.expected, string(NewPlatform(c.platform, c.osVersion))) + require.Equal(t, c.expected, string(NewPlatform(c.platform, c.osVersion)), c) } }) diff --git a/server/vulnerabilities/oval/parsed/object_info_state.go b/server/vulnerabilities/oval/parsed/object_info_state.go index 8fbc35d327..fc2457ac88 100644 --- a/server/vulnerabilities/oval/parsed/object_info_state.go +++ b/server/vulnerabilities/oval/parsed/object_info_state.go @@ -130,7 +130,8 @@ func (sta ObjectInfoState) EvalOSVersion(version fleet.OSVersion) (bool, error) if sta.Version != nil { var pVer string if version.Platform == "rhel" { - pName := strings.Trim(version.Name, " ") + version := ReplaceFedoraOSVersion(version.Name) + pName := strings.Trim(version, " ") pVer = pName[strings.LastIndex(pName, " ")+1:] } diff --git a/server/vulnerabilities/oval/parsed/utils.go b/server/vulnerabilities/oval/parsed/utils.go new file mode 100644 index 0000000000..d9fd835b2c --- /dev/null +++ b/server/vulnerabilities/oval/parsed/utils.go @@ -0,0 +1,33 @@ +package oval_parsed + +import ( + "regexp" + "strings" +) + +// ReplaceFedoraOSVersion Replaces `version` with the equivalent RHEL version, this is so that we +// can use RHEL OVAL definitions when scanning/running tests - note that even though Fedora is the +// upstream of RHEL, there is no 1:1 mapping between Fedora's versions and RHEL (for example RHEL 6 +// is based on both Fedora 12 and 13) - so this is an approximation. +// Examples: +// Red Hat Enterprise Linux 8.1.0 +// 'Fedora Linux 36.0.0' => 'Red Hat Enterprise Linux 9.0.0' +// 'Fedora Linux 12.0.0' => 'Red Hat Enterprise Linux 6.0.0' +// 'Fedora Linux 13.0.0' => 'Red Hat Enterprise Linux 6.0.0' +func ReplaceFedoraOSVersion(version string) string { + if strings.Index(version, "Fedora") != -1 { + rules := map[string]*regexp.Regexp{ + "Red Hat Enterprise Linux 6.0.0": regexp.MustCompile(`Fedora Linux (12|13|14|15|16|17|18)\.`), + "Red Hat Enterprise Linux 7.0.0": regexp.MustCompile(`Fedora Linux (19|20|21|22|23|24|25|26|27)\.`), + "Red Hat Enterprise Linux 8.0.0": regexp.MustCompile(`Fedora Linux (28|29|30|31|32|33)\.`), + "Red Hat Enterprise Linux 9.0.0": regexp.MustCompile(`Fedora Linux (34|35|36)\.`), + } + for rep, pattern := range rules { + if pattern.ReplaceAllString(version, rep) != version { + return rep + } + } + return "Red Hat Enterprise Linux 9.0.0" + } + return version +} diff --git a/server/vulnerabilities/oval/parser_test.go b/server/vulnerabilities/oval/parser_test.go index c4c5e2d3de..79c9e3ec57 100644 --- a/server/vulnerabilities/oval/parser_test.go +++ b/server/vulnerabilities/oval/parser_test.go @@ -641,7 +641,7 @@ func TestOvalParser(t *testing.T) { require.Equal(t, *testFour.States[0].SignatureKeyId, oval_parsed.NewObjectStateString("equals", "199e2f91fd431d51")) }) - t.Run("RHEL OVAL definitions work with CentOS", func(t *testing.T) { + t.Run("RHEL OVAL definitions work with RHEL based distros", func(t *testing.T) { r := strings.NewReader(rhelOvalXML) xmlResult, err := parseRhelXML(r) @@ -650,32 +650,33 @@ func TestOvalParser(t *testing.T) { result, err := mapToRhelResult(xmlResult) require.NoError(t, err) - centOS := fleet.OSVersion{ - Platform: "rhel", - Name: "CentOS Linux 7.9.2009", + testCases := []fleet.OSVersion{ + { + Platform: "rhel", + Name: "CentOS Linux 7.9.2009", + }, + { + Platform: "amzn", + Name: "Amazon Linux 2.0.0", + }, + { + Platform: "rhel", + Name: "Fedora Linux 19.0.0", + }, + { + Platform: "rhel", + Name: "Fedora Linux 20.0.0", + }, + { + Platform: "rhel", + Name: "Fedora Linux 21.0.0", + }, } - rEval, err := result.RpmVerifyFileTests[20221728047].Eval(centOS) - require.NoError(t, err) - require.True(t, rEval) - }) - - t.Run("RHEL OVAL definitions work with Amazon Distro", func(t *testing.T) { - r := strings.NewReader(rhelOvalXML) - - xmlResult, err := parseRhelXML(r) - require.NoError(t, err) - - result, err := mapToRhelResult(xmlResult) - require.NoError(t, err) - - amzDistro := fleet.OSVersion{ - Platform: "amzn", - Name: "Amazon Linux 2.0.0", + for _, tCase := range testCases { + rEval, err := result.RpmVerifyFileTests[20221728047].Eval(tCase) + require.NoError(t, err) + require.True(t, rEval, tCase) } - - rEval, err := result.RpmVerifyFileTests[20221728047].Eval(amzDistro) - require.NoError(t, err) - require.True(t, rEval) }) }