#!/usr/bin/env bash
# =============================================================================
#  ██╗    ██╗███████╗██████╗  ██████╗ █████╗ ██████╗ ███████╗██████╗  ██████╗  ██████╗ 
#  ██║    ██║██╔════╝██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝╚════██╗██╔════╝ ██╔═████╗
#  ██║ █╗ ██║█████╗  ██████╔╝██║     ███████║██████╔╝█████╗    █████╔╝███████╗ ██║██╔██║
#  ██║███╗██║██╔══╝  ██╔══██╗██║     ██╔══██║██╔══██╗██╔══╝    ╚═══██╗██╔═══██╗████╔╝██║
#  ╚███╔███╔╝███████╗██████╔╝╚██████╗██║  ██║██║  ██║███████╗██████╔╝╚██████╔╝╚██████╔╝
#   ╚══╝╚══╝ ╚══════╝╚═════╝  ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚═════╝  ╚═════╝  ╚═════╝ 
#  Patch Verification Script — CVE Remediation Status
#  Powered by WebCare360 | https://webcare360.com
#
# =============================================================================
# Covers:
#   CVE-2026-35414  OpenSSH
#   CVE-2026-23918  Apache HTTP Server (mod_http2 double-free, CVSS 8.8)
#   CVE-2026-41940  cPanel/WHM CRLF injection auth bypass (CVSS 9.8, CISA KEV)
#                   Fixed: 11.132.0.29 / 11.134.0.20 / 11.136.0.5  [Apr 28]
#   CVE-2026-29201  cPanel/WHM May 08 TSR
#   CVE-2026-29202  cPanel/WHM May 08 TSR (Perl code exec via plugin param)
#   CVE-2026-29203  cPanel/WHM May 08 TSR
#                   Per-branch fixed builds: verify at https://news.cpanel.com
#                   (TSR-2026-0002). Script checks upcp run date as a proxy.
#   CVE-2026-31431  Linux Kernel copy/fail privilege escalation
#   Dirty Frag       Linux kernel module mitigation review
#
# Compatible: CloudLinux 8 / AlmaLinux 8 / RHEL 8 + cPanel EasyApache4
# Usage:
#   chmod +x patch-verify.sh && ./patch-verify.sh
#   Exit: 0=all clear  1=failures exist  2=warnings only
# =============================================================================

set -uo pipefail

BOLD="\033[1m"
DIM="\033[2m"
GREEN="\033[32m"
RED="\033[31m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
MAGENTA="\033[35m"
RESET="\033[0m"

PASS=0
FAIL=0
WARN=0

line()    { printf "${BLUE}%s${RESET}\n" "======================================================================"; }
section() { echo; line; printf "${BOLD}${CYAN} %s${RESET}\n" "$1"; line; }
ok()      { printf "${GREEN}✅ PATCHED / OK${RESET}       %s\n" "$1"; PASS=$((PASS+1)); }
fail()    { printf "${RED}❌ NOT PATCHED${RESET}        %s\n" "$1"; FAIL=$((FAIL+1)); }
warn()    { printf "${YELLOW}⚠️  CHECK MANUALLY${RESET}    %s\n" "$1"; WARN=$((WARN+1)); }
info()    { printf "${DIM}   %s${RESET}\n" "$1"; }

# -----------------------------------------------------------------------------
# RPM-aware version comparison. Returns 0 if $1 >= $2.
# Uses rpmdev-vercmp when available (handles epoch + release correctly).
# Falls back to sort -V after normalising CloudLinux/LVE/EL suffixes.
# -----------------------------------------------------------------------------
ver_ge() {
    local v1 v2
    v1=$(echo "$1" | sed 's/\.x86_64$//; s/\.lve\././; s/\.cloudlinux\././; s/el8_[0-9]*/el8/g')
    v2=$(echo "$2" | sed 's/\.x86_64$//; s/\.lve\././; s/\.cloudlinux\././; s/el8_[0-9]*/el8/g')

    if command -v rpmdev-vercmp >/dev/null 2>&1; then
        local rc
        rpmdev-vercmp "$v1" "$v2" >/dev/null 2>&1; rc=$?
        # 0=equal, 11=v1 newer, 12=v2 newer
        [ "$rc" -ne 12 ]
    else
        [ "$(printf '%s\n%s\n' "$v2" "$v1" | sort -V | head -n1)" = "$v2" ]
    fi
}

# -----------------------------------------------------------------------------
# cPanel version comparison — pure integer field comparison, no regex stripping.
# Returns 0 if $1 >= $2.  Format: 11.134.0.23
# -----------------------------------------------------------------------------
cpanel_ver_ge() {
    local a="$1" b="$2"
    local IFS=.
    local -a ap bp
    read -r -a ap <<< "$a"
    read -r -a bp <<< "$b"
    local i
    for i in 0 1 2 3; do
        ap[$i]="${ap[$i]:-0}"
        bp[$i]="${bp[$i]:-0}"
        if [ "${ap[$i]}" -gt "${bp[$i]}" ] 2>/dev/null; then return 0; fi
        if [ "${ap[$i]}" -lt "${bp[$i]}" ] 2>/dev/null; then return 1; fi
    done
    return 0
}

# -----------------------------------------------------------------------------
# Returns the verified fixed cPanel build for CVE-2026-41940 (Apr 28 TSR).
# Source: Rapid7 advisory (https://www.rapid7.com/blog/post/etr-cve-2026-41940)
#         watchTowr Labs (confirmed 11.132.0.29 / 11.134.0.20 / 11.136.0.5)
# -----------------------------------------------------------------------------
cpanel_fixed_41940() {
    local p2 p3
    p2=$(echo "$1" | cut -d. -f2)
    p3=$(echo "$1" | cut -d. -f3)
    case "$p2.$p3" in
        136.0) echo "11.136.0.5"  ;;
        134.0) echo "11.134.0.20" ;;
        132.0) echo "11.132.0.29" ;;
        *)     echo "unknown"     ;;
    esac
}

# =============================================================================
# HEADER
# =============================================================================
clear 2>/dev/null || true

printf "${BOLD}${BLUE}"
cat << 'BANNER'
 ██╗    ██╗███████╗██████╗  ██████╗ █████╗ ██████╗ ███████╗██████╗  ██████╗  ██████╗ 
 ██║    ██║██╔════╝██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝╚════██╗██╔════╝ ██╔═████╗
 ██║ █╗ ██║█████╗  ██████╔╝██║     ███████║██████╔╝█████╗    █████╔╝███████╗ ██║██╔██║
 ██║███╗██║██╔══╝  ██╔══██╗██║     ██╔══██║██╔══██╗██╔══╝    ╚═══██╗██╔═══██╗████╔╝██║
 ╚███╔███╔╝███████╗██████╔╝╚██████╗██║  ██║██║  ██║███████╗██████╔╝╚██████╔╝╚██████╔╝
  ╚══╝╚══╝ ╚══════╝╚═════╝  ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚═════╝  ╚═════╝  ╚═════╝ 
BANNER
printf "${RESET}"
printf "${MAGENTA}${BOLD}  Patch Verification Script${RESET}   ${DIM}https://webcare360.com${RESET}\n"
line
printf "${BOLD}Host:${RESET}   %s\n" "$(hostname -f 2>/dev/null || hostname)"
printf "${BOLD}Date:${RESET}   %s\n" "$(date)"
printf "${BOLD}OS:${RESET}     %s\n" "$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2- | tr -d '"' || echo 'Unknown')"
printf "${BOLD}Kernel:${RESET} %s\n" "$(uname -r)"
line

# =============================================================================
section "OpenSSH / CVE-2026-35414"
# =============================================================================
OPENSSH_FIXED="8.0p1-29.el8_10"
OPENSSH_PKG=$(rpm -q openssh 2>/dev/null || true)

if echo "$OPENSSH_PKG" | grep -q "^openssh-"; then
    OPENSSH_VER=$(echo "$OPENSSH_PKG" | sed 's/^openssh-//; s/\.x86_64$//')
    info "Detected package: $OPENSSH_PKG"
    info "Fixed baseline:   openssh-${OPENSSH_FIXED} or newer"

    if ver_ge "$OPENSSH_VER" "$OPENSSH_FIXED"; then
        ok "OpenSSH is at or above the expected fixed package level."
    else
        fail "OpenSSH is below the expected fixed package level."
        info "Run: dnf update 'openssh*' -y"
    fi

    SSHD_VER=$(sshd -V 2>&1 | head -1 || true)
    [ -n "$SSHD_VER" ] && info "sshd reported: $SSHD_VER"
else
    warn "openssh package not found via rpm."
fi

# =============================================================================
section "Apache / CVE-2026-23918"
# =============================================================================
APACHE_FIXED="2.4.67"

if command -v httpd >/dev/null 2>&1; then
    APACHE_FULL=$(httpd -v 2>/dev/null | head -1 || true)
    APACHE_BUILT=$(httpd -v 2>/dev/null | sed -n '2p' || true)
    APACHE_VER=$(echo "$APACHE_FULL" | sed -n 's/.*Apache\/\([0-9.]*\).*/\1/p')
    APACHE_BIN=$(command -v httpd)
    APACHE_STATUS=$(systemctl is-active httpd 2>/dev/null || echo "unknown")

    info "Detected version: $APACHE_FULL"
    [ -n "$APACHE_BUILT" ] && info "$APACHE_BUILT"
    info "Binary path:      $APACHE_BIN"
    info "Service status:   $APACHE_STATUS"
    info "Fixed baseline:   Apache/${APACHE_FIXED} or newer"

    EA_PKG=$(rpm -q ea-apache24 2>/dev/null || true)
    if echo "$EA_PKG" | grep -q "^ea-apache24-"; then
        info "RPM package:      $EA_PKG"
        info "Package source:   cPanel EasyApache4-managed (cl-ea4 repo)"
    else
        RPM_HTTPD=$(rpm -q httpd 2>/dev/null || true)
        echo "$RPM_HTTPD" | grep -qi "not installed" \
            && info "RPM note: stock httpd not installed; cPanel EasyApache-managed." \
            || info "RPM package: $RPM_HTTPD"
    fi

    if [ -n "$APACHE_VER" ]; then
        if ver_ge "$APACHE_VER" "$APACHE_FIXED"; then
            ok "Apache is at or above the expected fixed version."
        else
            fail "Apache is below the expected fixed version."

            HTTP2_LOADED=$(httpd -M 2>/dev/null | grep -c "http2_module" || true)
            if [ "${HTTP2_LOADED:-0}" -eq 0 ]; then
                ok "mod_http2 is NOT loaded — CVE-2026-23918 attack vector not reachable."
                info "Compensating control only. Apply package fix when CloudLinux publishes 2.4.67."
                info "Issue: cl-ea4 epoch (1:) blocks install of EA4-c8 build (no epoch)."
                info "Monitor: dnf check-update ea-apache24"
            else
                warn "mod_http2 IS loaded — server is actively exposed to CVE-2026-23918."
                info "Emergency mitigation: add 'Protocols http/1.1' to httpd config, restart."
                info "Run: systemctl restart httpd"
            fi

            CL_AVAIL=$(dnf list available ea-apache24 2>/dev/null \
                | awk '/^ea-apache24\.x86_64/{print $2}' \
                | grep -E "^(1:)?2\.4\.6[7-9]|^(1:)?2\.[5-9]\." \
                || true)
            if [ -n "$CL_AVAIL" ]; then
                info "Patched package NOW available: ea-apache24-${CL_AVAIL}"
                info "Run: dnf update 'ea-apache24*' -y && systemctl restart httpd"
            else
                info "No patched ea-apache24 2.4.67 build in current repo yet."
            fi
        fi
    else
        warn "Unable to parse Apache version from 'httpd -v' output."
    fi
else
    warn "httpd binary not found in PATH."
fi

# =============================================================================
section "cPanel / WHM — CVE-2026-41940  [Apr 28, 2026 TSR]"
# CVSS 9.8 — CRLF injection auth bypass — CISA KEV — exploited since Feb 2026
# Fixed: 11.132.0.29 / 11.134.0.20 / 11.136.0.5 / WP2 136.1.7
# =============================================================================

if [ -f /usr/local/cpanel/version ]; then
    CPANEL_RAW=$(cat /usr/local/cpanel/version 2>/dev/null | tr -d '[:space:]' || true)
    CPANEL_DISPLAY=$(/usr/local/cpanel/cpanel -V 2>/dev/null || true)
    CPANEL_TIER=$(awk -F= '/^CPANEL=/{print $2}' /etc/cpupdate.conf 2>/dev/null || echo "not set")

    info "Detected cPanel version: $CPANEL_RAW"
    [ -n "$CPANEL_DISPLAY" ] && info "WHM build reported:      $CPANEL_DISPLAY"
    info "Configured tier:         $CPANEL_TIER"
    info "CVE severity:            CVSS 9.8 — CISA KEV listed"
    info "Exploitation window:     Active zero-day since ~Feb 23, 2026"

    FIXED_41940=$(cpanel_fixed_41940 "$CPANEL_RAW")

    if [ "$FIXED_41940" = "unknown" ]; then
        warn "cPanel branch not in CVE-2026-41940 verified fixed-version table."
        info "Branches covered: 11.132.x, 11.134.x, 11.136.x"
        info "Your version: $CPANEL_RAW — verify against https://news.cpanel.com"
        info "Run: /scripts/upcp --force && /scripts/restartsrv_cpsrvd --hard"
    else
        info "Fixed baseline (CVE-2026-41940): $FIXED_41940 or newer"
        if cpanel_ver_ge "$CPANEL_RAW" "$FIXED_41940"; then
            ok "cPanel/WHM is at or above the CVE-2026-41940 fixed baseline."
        else
            fail "cPanel/WHM is BELOW the CVE-2026-41940 fixed baseline. Patch immediately."
            info "Run: /scripts/upcp --force"
            info "Then: /scripts/restartsrv_cpsrvd --hard"
        fi
    fi
else
    warn "cPanel version file not found: /usr/local/cpanel/version"
fi

# =============================================================================
section "cPanel / WHM — CVE-2026-29201, 29202, 29203  [May 08, 2026 TSR]"
# Published: May 8, 2026 12:00 EST
# CVE-2026-29202: CWE-20 improper input validation in create_user plugin —
#   authenticated Perl code execution, affects 11.86.x through 11.136.x
# Per-branch exact fixed builds: https://news.cpanel.com (TSR-2026-0002)
# IMPORTANT: the version table in prior versions of this script was NOT sourced
# from the official TSR. Those numbers were unverified. This section uses the
# upcp log timestamp as a proxy until confirmed builds are added below.
# To add confirmed builds when available, populate the case block in the
# cpanel_fixed_may08() function and call cpanel_ver_ge() against it.
# =============================================================================

if [ -f /usr/local/cpanel/version ]; then
    CPANEL_RAW=$(cat /usr/local/cpanel/version 2>/dev/null | tr -d '[:space:]' || true)

    info "Checking: CVE-2026-29201, CVE-2026-29202, CVE-2026-29203"
    info "Affects:  cPanel 11.86.x through 11.136.x (per CVE-2026-29202 advisory)"
    info "Patch released: May 8, 2026 at 12:00 EST"
    info "Manual update: /scripts/upcp"

    # Find upcp log — 'latest' is a symlink on most installs but may be absent
    # on freshly provisioned or migrated servers. Fall back to newest file by mtime.
    UPCP_LOG="/var/cpanel/updatelogs/latest"
    if [ ! -f "$UPCP_LOG" ] && [ -d "/var/cpanel/updatelogs" ]; then
        UPCP_LOG=$(find /var/cpanel/updatelogs -maxdepth 1 -type f -name "*.log" \
            -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | awk '{print $2}' || true)
    fi
    # May 8, 2026 17:00 UTC = 1746723600 epoch
    MAY08_EPOCH=1746723600

    if [ -f "$UPCP_LOG" ]; then
        UPCP_MTIME=$(stat -c '%Y' "$UPCP_LOG" 2>/dev/null || echo "0")
        LAST_MTIME_HR=$(stat -c '%y' "$UPCP_LOG" 2>/dev/null || true)
        info "Latest upcp log modified: $LAST_MTIME_HR"

        if [ "${UPCP_MTIME:-0}" -ge "$MAY08_EPOCH" ] 2>/dev/null; then
            ok "upcp log updated on or after May 8, 2026 patch window."
            info "Verify exact build vs TSR-2026-0002: /usr/local/cpanel/cpanel -V"
            info "Official fixed builds: https://news.cpanel.com"
        else
            fail "upcp has NOT run since before the May 8, 2026 patch window."
            info "Run immediately: /scripts/upcp --force"
            info "Then restart: /scripts/restartsrv_cpsrvd --hard"
        fi


    else
        warn "No upcp log found at $UPCP_LOG — cannot confirm patch status."
        info "Run: /scripts/upcp --force"
    fi
fi

# =============================================================================
section "cPanel IOC / Session / Access Log Review"
# =============================================================================
SESSION_RAW="/var/cpanel/sessions/raw"

# --- Session token review ---
if [ -d "$SESSION_RAW" ]; then
    DENIED_COUNT=$(grep -rl "token_denied=1" "$SESSION_RAW" 2>/dev/null | wc -l)
    SUCCESS_COUNT=$(grep -rl "token_denied=0" "$SESSION_RAW" 2>/dev/null | wc -l)
    info "Denied token attempts:     $DENIED_COUNT"
    info "Successful token entries:  $SUCCESS_COUNT"

    if [ "$SUCCESS_COUNT" -eq 0 ]; then
        ok "No token_denied=0 entries found in raw session files."
    else
        warn "token_denied=0 entries found ($SUCCESS_COUNT) — review manually."
        info "Inspect: grep -rl 'token_denied=0' $SESSION_RAW"
    fi
else
    warn "$SESSION_RAW not found — skipping session token review."
fi

# --- .sorry ransomware artifact scan (CVE-2026-41940 indicator) ---
SORRY_COUNT=$(find /home /root /var/www 2>/dev/null -name "*.sorry" -maxdepth 6 | wc -l || true)
if [ "${SORRY_COUNT:-0}" -gt 0 ]; then
    fail ".sorry ransomware artifacts found (${SORRY_COUNT} files) — server may be compromised."
    info "This confirms CVE-2026-41940 exploitation. Patching alone is insufficient."
    info "Initiate incident response. Rotate all credentials."
    find /home /root /var/www 2>/dev/null -name "*.sorry" -maxdepth 6 | head -10 | sed 's/^/   /'
else
    ok "No .sorry ransomware artifacts found."
fi

# --- cPanel access log IOC patterns ---






# =============================================================================
section "Linux Kernel / CVE-2026-31431"
# =============================================================================
# Fixed baseline raised to 4.18.0-553.123.2.el8_10 — this is the AlmaLinux 8
# kernel that fixes BOTH CVE-2026-31431 (Copy Fail) AND CVE-2026-43284
# (Dirty Frag / xfrm-ESP). AlmaLinux production repos as of May 8 15:22 UTC.
# CloudLinux 8 rebuild is currently in the beta channel (cloudlinux-updates-testing).
KERNEL_RUNNING=$(uname -r)
# Fixed baselines covering CVE-2026-31431 (Copy Fail) + CVE-2026-43284 (Dirty Frag xfrm-ESP)
# Sources: AlmaLinux blog + CloudLinux advisory (May 8, 2026)
KERNEL_FIXED_EL8="4.18.0-553.123.2.el8_10.x86_64"
KERNEL_FIXED_EL9="5.14.0-611.54.3.el9_7.x86_64"

info "Running kernel:  $KERNEL_RUNNING"
KERNEL_PATCHED=0

_check_kernel() {
    local fixed="$1" stream="$2"
    local K_RUN K_FIX
    K_RUN=$(echo "$KERNEL_RUNNING" | sed 's/\.x86_64$//; s/\.lve\././; s/\.cloudlinux\././; s/el[0-9]*_[0-9]*/el/g')
    K_FIX=$(echo "$fixed"          | sed 's/\.x86_64$//;                                    s/el[0-9]*_[0-9]*/el/g')
    info "Fixed ${stream} baseline: $fixed or newer"
    info "Covers: CVE-2026-31431 (Copy Fail) + CVE-2026-43284 (Dirty Frag / xfrm-ESP)"
    if ver_ge "$K_RUN" "$K_FIX"; then
        ok "Running kernel is at or above the fixed level (${stream})."
        KERNEL_PATCHED=1
    else
        fail "Running kernel is below the fixed level for CVE-2026-31431 and CVE-2026-43284 (${stream})."
        if echo "$stream" | grep -q "EL8"; then
            info "AlmaLinux 8:    dnf update 'kernel*' -y && reboot"
            info "CloudLinux 8:   dnf --enablerepo=cloudlinux-updates-testing update 'kernel*' -y && reboot"
        else
            info "AlmaLinux/CL 9: dnf update 'kernel*' -y && reboot"
        fi
        info "KernelCare (no reboot): kcarectl --update"
        KERNEL_PATCHED=0
    fi
}

if grep -q 'VERSION_ID="9' /etc/os-release 2>/dev/null; then
    _check_kernel "$KERNEL_FIXED_EL9" "EL9"
elif grep -q 'VERSION_ID="8' /etc/os-release 2>/dev/null; then
    _check_kernel "$KERNEL_FIXED_EL8" "EL8"
else
    info "OS stream not detected as EL8 or EL9 — manual kernel verification required."
    warn "Verify kernel manually against your distribution's Dirty Frag advisory."
fi

# Detect installed-but-not-booted newer kernel
NEWEST_PKG=$(rpm -qa 'kernel-core-*' 2>/dev/null | sort -V | tail -1 \
    | sed 's/^kernel-core-//' || true)
if [ -n "$NEWEST_PKG" ]; then
    NEWEST_CLEAN=$(echo "$NEWEST_PKG"   | sed 's/\.x86_64$//; s/\.lve\././; s/\.cloudlinux\././; s/el[0-9]*_[0-9]*/el/g')
    RUNNING_CLEAN=$(echo "$KERNEL_RUNNING" | sed 's/\.x86_64$//; s/\.lve\././; s/\.cloudlinux\././; s/el[0-9]*_[0-9]*/el/g')
    if [ "$NEWEST_CLEAN" != "$RUNNING_CLEAN" ]; then
        warn "Newer kernel installed ($(echo "$NEWEST_PKG" | sed 's/\.x86_64$//')) but NOT running — reboot required."
    fi
fi

echo
printf "${BOLD}Installed kernel packages:${RESET}\n"
rpm -qa | grep -E '^kernel-core|^kernel-headers|^kernel-tools|^kernel-devel' \
    | sort -V | sed 's/^/   /' || true

# =============================================================================
section "Dirty Frag / CVE-2026-43284 + CVE-2026-43500"
# CVE-2026-43284 — xfrm-ESP (esp4/esp6): kernel patch available EL8 + EL9
# CVE-2026-43500 — RxRPC: NO kernel patch in any tree as of May 9, 2026
# AlmaLinux 8: rxrpc does not ship, so only CVE-2026-43284 applies.
# AlmaLinux/CL 9: both CVEs apply only if rxrpc module is present.
# Logic:
#   PASS if: kernel is patched AND vulnerable modules are not loaded
#   PASS if: blacklist conf is complete AND modules are not loaded
#   FAIL if: kernel is unpatched AND no blacklist AND/OR modules are loaded
# =============================================================================
DIRTYFRAG_CONF="/etc/modprobe.d/dirtyfrag.conf"
KERNEL_PATCHED="${KERNEL_PATCHED:-0}"

info "CVE-2026-43284 (xfrm-ESP): kernel patch available (EL8: 4.18.0-553.123.2 / EL9: 5.14.0-611.54.3)"
info "CVE-2026-43500 (rxrpc):    NO kernel patch available in any tree as of May 9, 2026"

# --- Check for KernelCare livepatch ---
KERNELCARE_ACTIVE=0
if command -v kcarectl >/dev/null 2>&1; then
    KC_LEVEL=$(kcarectl --info 2>/dev/null | awk -F: '/patch level/{gsub(/ /,"",$2); print $2}' | head -1 || true)
    if [ "${KC_LEVEL:-0}" -gt 0 ] 2>/dev/null; then
        ok "KernelCare livepatch active (patch level ${KC_LEVEL}) — CVE-2026-43284 covered without reboot."
        KERNELCARE_ACTIVE=1
    else
        info "KernelCare installed but patch level 0 — run: kcarectl --update"
    fi
fi

# --- Check which vulnerable modules are actually loaded ---
LOADED_MODS=$(lsmod 2>/dev/null | awk '{print $1}' \
    | grep -E '^(esp4|esp6|rxrpc|xfrm_user|ipcomp4|ipcomp6)$' || true)

if [ -n "$LOADED_MODS" ]; then
    # Modules loaded = actively exploitable regardless of kernel or blacklist
    fail "Dirty Frag vulnerable modules are currently loaded: $(echo "$LOADED_MODS" | tr '\n' ' ')"
    info "Unload immediately: rmmod $(echo "$LOADED_MODS" | tr '\n' ' ') 2>/dev/null; true"
    info "If rmmod fails (module in use), a reboot is required."
    info "Apply blacklist to prevent reload:"
    info "  sudo sh -c \"printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\ninstall xfrm_user /bin/false\ninstall ipcomp4 /bin/false\ninstall ipcomp6 /bin/false\n' > $DIRTYFRAG_CONF\""
else
    ok "No Dirty Frag vulnerable modules loaded (esp4, esp6, rxrpc, xfrm_user, ipcomp4, ipcomp6)."

    # Modules not loaded. Now assess whether protection is adequate.
    if [ "${KERNEL_PATCHED}" -eq 1 ] || [ "${KERNELCARE_ACTIVE}" -eq 1 ]; then
        # Kernel is patched and modules are not loaded — fully protected.
        ok "Kernel is patched and vulnerable modules are not loaded — Dirty Frag fully mitigated."
        # Blacklist is still best practice to prevent accidental future loading.
        if [ ! -f "$DIRTYFRAG_CONF" ]; then
            info "Recommended hardening (not required): add module blacklist to prevent future loading:"
            info "  sudo sh -c \"printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\ninstall xfrm_user /bin/false\ninstall ipcomp4 /bin/false\ninstall ipcomp6 /bin/false\n' > $DIRTYFRAG_CONF\""
        fi
    else
        # Kernel is unpatched and modules are not loaded — blacklist is the only protection.
        if [ -f "$DIRTYFRAG_CONF" ]; then
            ESP4=$(grep  -Ec '^[[:space:]]*install[[:space:]]+esp4[[:space:]]+/bin/false'      "$DIRTYFRAG_CONF" || true)
            ESP6=$(grep  -Ec '^[[:space:]]*install[[:space:]]+esp6[[:space:]]+/bin/false'      "$DIRTYFRAG_CONF" || true)
            RXRPC=$(grep -Ec '^[[:space:]]*install[[:space:]]+rxrpc[[:space:]]+/bin/false'     "$DIRTYFRAG_CONF" || true)
            XFRM=$(grep  -Ec '^[[:space:]]*install[[:space:]]+xfrm_user[[:space:]]+/bin/false' "$DIRTYFRAG_CONF" || true)
            IPC4=$(grep  -Ec '^[[:space:]]*install[[:space:]]+ipcomp4[[:space:]]+/bin/false'   "$DIRTYFRAG_CONF" || true)
            IPC6=$(grep  -Ec '^[[:space:]]*install[[:space:]]+ipcomp6[[:space:]]+/bin/false'   "$DIRTYFRAG_CONF" || true)
            MISSING=""
            [ "${ESP4:-0}"  -lt 1 ] && MISSING="$MISSING esp4"
            [ "${ESP6:-0}"  -lt 1 ] && MISSING="$MISSING esp6"
            [ "${RXRPC:-0}" -lt 1 ] && MISSING="$MISSING rxrpc"
            [ "${XFRM:-0}"  -lt 1 ] && MISSING="$MISSING xfrm_user"
            [ "${IPC4:-0}"  -lt 1 ] && MISSING="$MISSING ipcomp4"
            [ "${IPC6:-0}"  -lt 1 ] && MISSING="$MISSING ipcomp6"
            if [ -z "$MISSING" ]; then
                ok "Dirty Frag module blacklist complete — compensating control active (kernel not yet patched)."
                info "Apply kernel patch when available: dnf update 'kernel*' -y && reboot"
            else
                fail "Kernel unpatched and blacklist incomplete — missing:${MISSING}"
                info "  sudo sh -c \"printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\ninstall xfrm_user /bin/false\ninstall ipcomp4 /bin/false\ninstall ipcomp6 /bin/false\n' > $DIRTYFRAG_CONF\""
            fi
        else
            fail "Kernel unpatched and no module blacklist found: $DIRTYFRAG_CONF"
            info "CVE-2026-43500 (rxrpc) has no kernel patch — blacklist is the only protection."
            info "Apply now:"
            info "  sudo sh -c \"printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\ninstall xfrm_user /bin/false\ninstall ipcomp4 /bin/false\ninstall ipcomp6 /bin/false\n' > $DIRTYFRAG_CONF\""
            info "  rmmod esp4 esp6 rxrpc xfrm_user ipcomp4 ipcomp6 2>/dev/null; true"
        fi
    fi
fi

info "Post-mitigation best practice: flush page cache to evict any attacker-written pages:"
info "  echo 3 > /proc/sys/vm/drop_caches"

# =============================================================================
section "cPanel / WHM Port Exposure Review"
# =============================================================================
if command -v ss >/dev/null 2>&1; then
    # Extract only port, local address, and first PID — the full ss output
    # dumps every cpsrvd worker PID which is unreadably long and useless.
    CPANEL_PORTS=$(ss -lntp 2>/dev/null \
        | grep -E ':(2082|2083|2086|2087|2095|2096)[[:space:]]' \
        | awk '{
            # Extract port from local address field (col 4)
            split($4, addr, ":");
            port = addr[length(addr)];
            # Extract just the first pid= value from the users field
            match($0, /pid=([0-9]+)/, arr);
            pid = arr[1] ? arr[1] : "?";
            printf "   port %-5s  listen %-22s  pid %s (cpsrvd)\n", port, $4, pid
          }' \
        || true)
    if [ -n "$CPANEL_PORTS" ]; then
        warn "cPanel/WHM/Webmail ports are publicly accessible on this host."
        echo "$CPANEL_PORTS"
        info "Restrict ports 2082/2083/2086/2087/2095/2096 to trusted IPs at the firewall."
        info "Example: firewall-cmd --add-rich-rule='rule family=ipv4 source address=TRUSTED_IP port port=2087 protocol=tcp accept'"
    else
        ok "No cPanel/WHM/Webmail listeners found on standard ports."
    fi
else
    warn "ss not available — skipping port exposure check."
fi

# =============================================================================
section "Recommended Immediate Commands"
# =============================================================================
[ -f /usr/local/cpanel/version ] && \
    printf "${DIM}   Current cPanel: %s${RESET}\n" "$(cat /usr/local/cpanel/version 2>/dev/null)"

echo
printf "${BOLD}cPanel update and verification:${RESET}\n"
printf "   %s\n" "/scripts/upcp --force"
printf "   %s\n" "/usr/local/cpanel/cpanel -V"
printf "   %s\n" "/scripts/restartsrv_cpsrvd --hard"
printf "   %s\n" "tail -200 /var/cpanel/updatelogs/latest"
echo
printf "${BOLD}Package updates:${RESET}\n"
printf "   %s\n" "dnf check-update"
printf "   %s\n" "dnf update 'ea-apache24*' 'openssh*' 'kernel*' -y"
printf "   %s\n" "# CloudLinux 8 — Dirty Frag kernel (beta channel):"
printf "   %s\n" "dnf --enablerepo=cloudlinux-updates-testing update 'kernel*' -y && reboot"
printf "   %s\n" "# KernelCare livepatch (no reboot required):"
printf "   %s\n" "kcarectl --update"
echo
printf "${BOLD}Dirty Frag module blacklist (required — CVE-2026-43500 has no kernel patch):${RESET}\n"
printf "   %s\n" "sudo sh -c \"printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\ninstall xfrm_user /bin/false\ninstall ipcomp4 /bin/false\ninstall ipcomp6 /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf\""
printf "   %s\n" "rmmod esp4 esp6 rxrpc xfrm_user ipcomp4 ipcomp6 2>/dev/null; true"
printf "   %s\n" "echo 3 > /proc/sys/vm/drop_caches"
echo
printf "${BOLD}Confirm May 08 TSR fixed build numbers:${RESET}\n"
printf "   %s\n" "https://news.cpanel.com   (TSR-2026-0002)"
echo
printf "${BOLD}Ransomware artifact scan:${RESET}\n"
printf "   %s\n" "find /home /root -name '*.sorry' 2>/dev/null"

# =============================================================================
section "Final Summary"
# =============================================================================
printf "${GREEN}✅ Patched / OK:${RESET}       %s\n" "$PASS"
printf "${RED}❌ Not patched:${RESET}        %s\n" "$FAIL"
printf "${YELLOW}⚠️  Manual checks:${RESET}     %s\n" "$WARN"
echo

if [ "$FAIL" -eq 0 ] && [ "$WARN" -eq 0 ]; then
    printf "${GREEN}${BOLD}FINAL STATUS: ALL CHECKS PASSED — FULLY PATCHED / REMEDIATED${RESET}\n"
    EXIT_CODE=0
elif [ "$FAIL" -eq 0 ]; then
    printf "${YELLOW}${BOLD}FINAL STATUS: PATCHES LOOK GOOD, BUT MANUAL REVIEW ITEMS EXIST${RESET}\n"
    EXIT_CODE=2
else
    printf "${RED}${BOLD}FINAL STATUS: ONE OR MORE ITEMS ARE NOT PATCHED${RESET}\n"
    EXIT_CODE=1
fi

line
printf "${DIM}   WebCare360 Patch Verification | https://webcare360.com${RESET}\n"
line

exit "$EXIT_CODE"