@@ -67,6 +67,50 @@ func checkOSVersion() []Finding {
6767 }}
6868}
6969
70+ // ─── RHEL Backport Helpers ───
71+
72+ // isRHELBased returns true for distros that backport security patches
73+ // to older version numbers (RHEL, AlmaLinux, Rocky, CentOS, Oracle Linux).
74+ func isRHELBased () bool {
75+ data , err := os .ReadFile ("/etc/os-release" )
76+ if err != nil {
77+ return false
78+ }
79+ id := strings .ToLower (osReleaseValue (string (data ), "ID" ))
80+ idLike := strings .ToLower (osReleaseValue (string (data ), "ID_LIKE" ))
81+ rhelIDs := []string {"rhel" , "almalinux" , "rocky" , "centos" , "ol" , "amzn" , "fedora" }
82+ for _ , rid := range rhelIDs {
83+ if id == rid || strings .Contains (idLike , rid ) || strings .Contains (idLike , "rhel" ) {
84+ return true
85+ }
86+ }
87+ return false
88+ }
89+
90+ // hasPackageUpdate checks if a specific package has an available update
91+ // using dnf or yum. Returns true if an update IS available.
92+ func hasPackageUpdate (pkg string ) bool {
93+ // dnf check-update exits 100 if updates available, 0 if none
94+ if _ , err := exec .LookPath ("dnf" ); err == nil {
95+ cmd := exec .Command ("dnf" , "check-update" , "--quiet" , pkg )
96+ err := cmd .Run ()
97+ // exit 100 = updates available, exit 0 = no updates
98+ if exitErr , ok := err .(* exec.ExitError ); ok {
99+ return exitErr .ExitCode () == 100
100+ }
101+ return false // exit 0 = no updates
102+ }
103+ if _ , err := exec .LookPath ("yum" ); err == nil {
104+ cmd := exec .Command ("yum" , "check-update" , "--quiet" , pkg )
105+ err := cmd .Run ()
106+ if exitErr , ok := err .(* exec.ExitError ); ok {
107+ return exitErr .ExitCode () == 100
108+ }
109+ return false
110+ }
111+ return false
112+ }
113+
70114// ─── Kernel Version Check ───
71115
72116func checkKernelVersion () []Finding {
@@ -94,9 +138,16 @@ func checkKernelVersion() []Finding {
94138 passed = false
95139 desc += " — very old kernel, known vulnerabilities"
96140 } else if major < 5 || (major == 5 && minor < 4 ) {
97- severity = "medium"
98- passed = false
99- desc += " — outdated kernel"
141+ // On RHEL-based distros, check if the kernel has a pending update
142+ // RHEL 8 ships 4.18.x with all security patches backported
143+ if isRHELBased () && ! hasPackageUpdate ("kernel" ) {
144+ desc += " — latest available for this OS (security patches backported)"
145+ passed = true
146+ } else {
147+ severity = "medium"
148+ passed = false
149+ desc += " — outdated kernel"
150+ }
100151 } else {
101152 desc += " — up to date"
102153 }
@@ -317,13 +368,20 @@ func checkOpenSSHVersion() []Finding {
317368 desc := fmt .Sprintf ("OpenSSH %s" , version )
318369
319370 if major < 8 {
371+ // Even on RHEL, OpenSSH < 8 is truly old (RHEL 7 = EOL)
320372 severity = "high"
321373 passed = false
322374 desc += " — very old, known vulnerabilities"
323375 } else if major == 8 && minor < 9 {
324- severity = "medium"
325- passed = false
326- desc += " — outdated"
376+ // RHEL 8 ships OpenSSH 8.0 with backported security patches
377+ if isRHELBased () && ! hasPackageUpdate ("openssh-server" ) {
378+ desc += " — latest available for this OS (security patches backported)"
379+ passed = true
380+ } else {
381+ severity = "medium"
382+ passed = false
383+ desc += " — outdated"
384+ }
327385 } else {
328386 desc += " — up to date"
329387 }
@@ -353,13 +411,22 @@ func checkWebServerVersion() []Finding {
353411 desc := fmt .Sprintf ("Nginx %s" , version )
354412
355413 if major == 1 && minor < 22 {
356- severity = "high"
357- passed = false
358- desc += " — outdated, known vulnerabilities"
414+ if isRHELBased () && ! hasPackageUpdate ("nginx" ) {
415+ desc += " — latest available for this OS"
416+ // pass — no update available in repos
417+ } else {
418+ severity = "high"
419+ passed = false
420+ desc += " — outdated, known vulnerabilities"
421+ }
359422 } else if major == 1 && minor < 24 {
360- severity = "medium"
361- passed = false
362- desc += " — consider upgrading"
423+ if isRHELBased () && ! hasPackageUpdate ("nginx" ) {
424+ desc += " — latest available for this OS"
425+ } else {
426+ severity = "medium"
427+ passed = false
428+ desc += " — consider upgrading"
429+ }
363430 } else {
364431 desc += " — up to date"
365432 }
@@ -396,9 +463,13 @@ func checkWebServerVersion() []Finding {
396463
397464 _ = minor // Apache is always 2.4.x nowadays
398465 if patch < 54 {
399- severity = "high"
400- passed = false
401- desc += " — outdated, known vulnerabilities"
466+ if isRHELBased () && ! hasPackageUpdate ("httpd" ) {
467+ desc += " — latest available for this OS"
468+ } else {
469+ severity = "high"
470+ passed = false
471+ desc += " — outdated, known vulnerabilities"
472+ }
402473 } else {
403474 desc += " — up to date"
404475 }
0 commit comments