Feature 6242: Use oval to detect vulnerabilities on Fedora hosts (#6330)

* Feature 6242: Scan Fedora hosts using OVAL definitions
This commit is contained in:
Juan Fernandez 2022-06-24 11:02:51 -04:00 committed by GitHub
parent c4aeebed1b
commit 79bf51b03c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 29 deletions

View file

@ -0,0 +1 @@
- Add support for scanning Fedora hosts using OVAL definitions.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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