Scan goval-dict for rhel kernel vulnerabilities(#39749)

This commit is contained in:
Tim Lee 2026-02-12 15:21:59 -07:00 committed by GitHub
parent 573bf877fe
commit fb2ddde9bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 105 additions and 5 deletions

View file

@ -0,0 +1 @@
- added ability to scan for kernel vulnerabilities on RHEL based hosts

View file

@ -119,7 +119,7 @@ const generateDefaultTableHeaders = (
tipContent={
<>
Vulnerabilities on Linux are currently supported <br />
for Ubuntu, Debian, and Amazon Linux.
for Ubuntu, Debian, and RHEL based systems.
</>
}
>

View file

@ -3060,6 +3060,10 @@ func (ds *Datastore) ListSoftwareForVulnDetection(
baseSQL += "JOIN host_software hs ON s.id = hs.software_id "
}
if filters.KernelsOnly {
baseSQL += "JOIN software_titles st ON s.title_id = st.id "
}
conditions := []string{}
if filters.HostID != nil {
@ -3077,6 +3081,10 @@ func (ds *Datastore) ListSoftwareForVulnDetection(
args = append(args, filters.Source)
}
if filters.KernelsOnly {
conditions = append(conditions, "st.is_kernel = 1")
}
if len(conditions) > 0 {
sqlstmt = baseSQL + "WHERE " + strings.Join(conditions, " AND ")
} else {

View file

@ -2804,6 +2804,36 @@ func testListSoftwareForVulnDetection(t *testing.T, ds *Datastore) {
require.Equal(t, "baz", result[0].Name)
require.Equal(t, "biz", result[1].Name)
})
t.Run("KernelsOnly filter returns only kernel software", func(t *testing.T) {
ctx := context.Background()
host := test.NewHost(t, ds, "host_kernel_test", "", "hostkernelkey", "hostkerneluuid", time.Now())
host.Platform = "rhel"
require.NoError(t, ds.UpdateHost(ctx, host))
software := []fleet.Software{
{Name: "kernel", Version: "5.14.0-503.38.1.el9_5", Release: "", Arch: "x86_64", Source: "rpm_packages", IsKernel: true},
{Name: "kernel-modules", Version: "5.14.0", Release: "503.38.1.el9_5", Arch: "x86_64", Source: "rpm_packages", IsKernel: false},
{Name: "bash", Version: "5.1.8", Release: "6.el9_1", Arch: "x86_64", Source: "rpm_packages", IsKernel: false},
{Name: "openssl", Version: "3.0.7", Release: "18.el9_2", Arch: "x86_64", Source: "rpm_packages", IsKernel: false},
}
_, err := ds.UpdateHostSoftware(ctx, host.ID, software)
require.NoError(t, err)
// Test KernelsOnly filter
filter := fleet.VulnSoftwareFilter{HostID: &host.ID, KernelsOnly: true}
result, err := ds.ListSoftwareForVulnDetection(ctx, filter)
require.NoError(t, err)
require.Len(t, result, 1)
require.Equal(t, "kernel", result[0].Name)
// Verify non-kernel filter returns all software
filter = fleet.VulnSoftwareFilter{HostID: &host.ID, KernelsOnly: false}
result, err = ds.ListSoftwareForVulnDetection(ctx, filter)
require.NoError(t, err)
require.Len(t, result, 4)
})
}
func testSoftwareByIDNoDuplicatedVulns(t *testing.T, ds *Datastore) {

View file

@ -255,9 +255,10 @@ type VulnerableSoftware struct {
}
type VulnSoftwareFilter struct {
HostID *uint
Name string // LIKE filter
Source string // exact match
HostID *uint
Name string // LIKE filter
Source string // exact match
KernelsOnly bool // filter to kernel packages only (for RHEL goval-dictionary scanning)
}
type SliceString []string

View file

@ -2275,6 +2275,13 @@ func MutateSoftwareOnIngestion(s *fleet.Software, logger log.Logger) {
}
}
}
// For RHEL kernels, join version and release to match OVAL format.
// See server/vulnerabilities/goval_dictionary/database.Eval
if s != nil && s.Source == "rpm_packages" && s.Name == rpmKernelName && s.Release != "" {
s.Version = fmt.Sprintf("%s-%s", s.Version, s.Release)
s.Release = "" // Clear release to avoid issues with vulnerability matching
}
}
// shouldRemoveSoftware returns whether or not we should remove the given Software item from this

View file

@ -40,6 +40,11 @@ func Analyze(
return nil, err
}
// For kernel-only platforms (e.g., RHEL), we only scan kernel packages via goval-dictionary.
// Non-kernel packages are scanned via regular OVAL processing. This keeps the testing
// surface smaller. We can consider expanding scope to all packages in the future if needed.
kernelsOnly := platform.IsGovalDictionaryKernelOnly()
// Since hosts and software have a M:N relationship, the following sets are used to
// avoid doing duplicated inserts/delete operations (a vulnerable software might be
// present in many hosts).
@ -61,7 +66,10 @@ func Analyze(
foundInBatch := make(map[uint][]fleet.SoftwareVulnerability)
for _, hostID := range hostIDs {
hostID := hostID
software, err := ds.ListSoftwareForVulnDetection(ctx, fleet.VulnSoftwareFilter{HostID: &hostID})
software, err := ds.ListSoftwareForVulnDetection(ctx, fleet.VulnSoftwareFilter{
HostID: &hostID,
KernelsOnly: kernelsOnly,
})
if err != nil {
return nil, err
}

View file

@ -25,6 +25,17 @@ var SupportedGovalPlatforms = []string{
"amzn_02",
"amzn_2022",
"amzn_2023",
"rhel_07",
"rhel_08",
"rhel_09",
}
// GovalKernelOnlyPlatforms are platforms where goval-dictionary is used only for kernel vulnerability scanning.
// These platforms use the regular OVAL scanning for non-kernel packages.
var GovalKernelOnlyPlatforms = []string{
"rhel_07",
"rhel_08",
"rhel_09",
}
// getMajorMinorVer returns the major and minor version of an 'os_version'.
@ -129,6 +140,17 @@ func (op Platform) IsGovalDictionarySupported() bool {
return false
}
// IsGovalDictionaryKernelOnly returns true if this platform uses goval-dictionary
// only for kernel vulnerability scanning (non-kernel packages use regular OVAL).
func (op Platform) IsGovalDictionaryKernelOnly() bool {
for _, p := range GovalKernelOnlyPlatforms {
if strings.HasPrefix(string(op), p) {
return true
}
}
return false
}
// IsUbuntu checks whether the current Platform targets Ubuntu.
func (op Platform) IsUbuntu() bool {
return strings.HasPrefix(string(op), "ubuntu")

View file

@ -104,4 +104,27 @@ func TestOvalPlatform(t *testing.T) {
require.Equal(t, c.expected, plat.ToGovalDatabaseFilename())
}
})
t.Run("IsGovalDictionaryKernelOnly", func(t *testing.T) {
cases := []struct {
platform string
osVersion string
kernelOnly bool
}{
// Amazon Linux is NOT kernel-only (full goval-dictionary scanning)
{"amzn", "Amazon Linux 1.0.0", false},
{"amzn", "Amazon Linux 2.0.0", false},
{"amzn", "Amazon Linux 2023.0.0", false},
// RHEL is kernel-only (only kernel packages scanned via goval-dictionary)
{"rhel", "CentOS Linux 7.9.2009", true},
{"rhel", "CentOS Linux 8.3.2011", true},
{"rhel", "Red Hat Enterprise Linux 9.0.0", true},
// Ubuntu is not supported by goval-dictionary at all
{"ubuntu", "Ubuntu 20.4.0", false},
}
for _, c := range cases {
plat := NewPlatform(c.platform, c.osVersion)
require.Equal(t, c.kernelOnly, plat.IsGovalDictionaryKernelOnly(), "platform=%s, osVersion=%s", c.platform, c.osVersion)
}
})
}