Skip to content

Conversation

@hyaku0121
Copy link
Contributor

@hyaku0121 hyaku0121 commented Dec 11, 2025

Closes #128

Summary

I have implemented the System Health module including Security, Disk, Performance, and Update checks.

Features Implemented

  • Health Scoring: Calculates overall score based on multiple factors.
  • Multiple Checks: Implemented checks for Security (Firewall/SSH), Disk usage, System Performance, and Updates.
  • Actionable Recommendations: Provides specific commands to fix issues.
  • Unit Tests: Passed all tests in tests/test_health_monitor.py.

Note on Advanced Requirements (Auto-fix & History)

To avoid altering the core architecture of the HealthCheck base class, I implemented the Automated Fixes and Trend Tracking features in a standalone utility script located at scripts/verify_ubuntu_compatibility.py.

This script demonstrates:

  1. Saving execution history to JSON (Trend Tracking).
  2. Automatically applying fixes for UFW and SSH config (One-click fixes).

You can run this script to verify these capabilities immediately.

Summary by CodeRabbit

  • New Features

    • Added a CLI health command reporting system health (disk, performance, security, updates) with scores, details, recommendations and persisted history.
    • Added a standalone Ubuntu security verification script with optional automated fixes and trend/history display.
  • Bug Fixes / Improvements

    • Improved notification management messages, switched to public config save, and added DND time (HH:MM) validation.
  • Tests

    • Added comprehensive tests for health checks and monitor aggregation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Warning

Rate limit exceeded

@hyaku0121 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 29 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between dff2092 and 618e075.

📒 Files selected for processing (5)
  • cortex/health/checks/disk.py (1 hunks)
  • cortex/health/checks/security.py (1 hunks)
  • cortex/health/checks/updates.py (1 hunks)
  • cortex/health/monitor.py (1 hunks)
  • scripts/verify_ubuntu_compatibility.py (1 hunks)

Walkthrough

Adds a health monitoring subsystem with four system checks (disk, performance, security, updates), a HealthMonitor orchestrator that aggregates weighted scores and persists history, CLI integration via a new health command, a standalone Ubuntu verification script, and unit tests for the health components.

Changes

Cohort / File(s) Change Summary
Health Monitor Core
cortex/health/monitor.py
Adds CheckResult dataclass, HealthCheck ABC, and HealthMonitor to register checks, run them with exception isolation, compute a weighted aggregate score, build a timestamped report, and persist history (trim to last 100).
Health Checks
cortex/health/checks/disk.py, cortex/health/checks/performance.py, cortex/health/checks/security.py, cortex/health/checks/updates.py
Implements four concrete checks: DiskCheck (disk usage thresholds), PerformanceCheck (load average & memory usage), SecurityCheck (UFW status & sshd PermitRootLogin), and UpdateCheck (apt upgradable packages & security update counting). Each returns CheckResult with score/status/details/recommendation and weight.
CLI Integration & Notifications
cortex/cli.py
Adds CortexCLI.health(self, args) and routes the new health subcommand in CLI parsing and help output; prints formatted health score, factors, and recommendations. Replaces private config saves with save_config() in notify actions, adds DND time validation, and minor messaging updates.
Ubuntu Verification Script
scripts/verify_ubuntu_compatibility.py
New standalone script to verify and optionally fix Ubuntu firewall and SSH config issues, persist a short history, show trends, and offer interactive fixes.
Tests
tests/test_health_monitor.py
Adds unit tests for Disk, Performance, Security, Update checks and HealthMonitor aggregation using mocks for system interactions.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as CortexCLI
    participant Monitor as HealthMonitor
    participant Checks as HealthChecks
    participant System as OS/Commands/Files
    participant History as History File

    User->>CLI: run `cortex health`
    CLI->>Monitor: instantiate & register checks
    CLI->>Monitor: run_all()
    loop per-check (iterative)
        Monitor->>Checks: <check>.run()
        Checks->>System: perform system queries (systemctl, files, apt, disk_usage, getloadavg)
        System-->>Checks: outputs / exceptions
        Checks-->>Monitor: CheckResult (or error handled)
    end
    Monitor->>Monitor: aggregate weighted scores, build report
    Monitor->>History: _save_history(report)
    History-->>Monitor: write JSON (trim to last 100)
    Monitor-->>CLI: report dict
    CLI-->>User: formatted health output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

  • Review focus:
    • HealthMonitor.run_all() aggregation, weight handling, and exception isolation.
    • System interaction and error handling in SecurityCheck and PerformanceCheck (systemctl, sshd_config, /proc/meminfo).
    • Parsing logic in UpdateCheck (apt output) and DiskCheck thresholds.
    • CLI integration for health command and notification save_config() usage.
    • Tests: ensure mocks accurately reflect real command/file behaviors and edge cases.

Possibly related issues

Suggested labels

enhancement

Poem

🐰 I hopped through bytes and checked each wheel,
Disk, load, and firewall — I sniffed for real.
Scores weighed like carrots on a tidy scale,
History tucked safe in a burrowed trail,
Hop, report, recommend — a rabbit's system tale.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add system health monitoring module (#128)' directly and concisely summarizes the main change: introduction of a new system health monitoring module with multiple health checks (Security, Disk, Performance, Updates).
Description check ✅ Passed The PR description includes a summary, features implemented, and notes on advanced requirements, but lacks explicit confirmation that tests pass and PR title format validation.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (15)
cortex/health/checks/updates.py (3)

4-5: Add docstrings for public class and method.

As per coding guidelines, docstrings are required for all public APIs. The UpdateCheck class and run method lack documentation.

 class UpdateCheck(HealthCheck):
+    """Check for pending system updates and security patches."""
+    
     def run(self) -> CheckResult:
+        """
+        Run the update check by querying apt for upgradable packages.
+        
+        Returns:
+            CheckResult with score based on pending updates count.
+        """
         score = 100

37-38: Avoid silently swallowing exceptions.

The bare except Exception: pass hides potential issues like subprocess failures or parsing errors. Consider logging the error or returning a result that indicates the check could not complete reliably.

-        except Exception:
-            pass # Ignore errors
+        except Exception as e:
+            # Log or handle gracefully; return indeterminate result
+            return CheckResult(
+                name="System Updates",
+                category="updates",
+                score=100,
+                status="SKIP",
+                details=f"Check failed: {e}",
+                weight=0.30
+            )

14-17: Add timeout to subprocess.run to prevent hangs.

If apt hangs (e.g., waiting for a lock), the health check will block indefinitely. Consider adding a timeout.

             res = subprocess.run(
                 ["apt", "list", "--upgradable"], 
-                capture_output=True, text=True
+                capture_output=True, text=True, timeout=30
             )
scripts/verify_ubuntu_compatibility.py (4)

8-9: Use absolute path for history file.

HISTORY_FILE is a relative path, so the JSON file will be written to whatever directory the script is run from. Consider storing it in a predictable location like the user's home directory.

+import pathlib
+
 # File name to store history data
-HISTORY_FILE = "security_history.json"
+HISTORY_FILE = pathlib.Path.home() / ".cortex" / "security_history.json"

You'll also need to ensure the directory exists before writing:

HISTORY_FILE.parent.mkdir(exist_ok=True)

62-62: Remove unnecessary f-string.

This f-string has no placeholders. Use a regular string instead.

         else:
-            print(f"    Trend: ➡️ Stable")
+            print("    Trend: ➡️ Stable")

138-239: Refactor verify_security_logic to reduce cognitive complexity.

Static analysis reports cognitive complexity of 33 (limit is 15). Extract the firewall check and SSH check logic into separate helper functions to improve readability and maintainability.

def _check_firewall() -> tuple[bool, bool]:
    """Check UFW status. Returns (is_active, needs_fix)."""
    print("\n[1] Checking Firewall (UFW)...")
    try:
        print("    Running: systemctl is-active ufw")
        res = subprocess.run(
            ["systemctl", "is-active", "ufw"],
            capture_output=True, text=True, timeout=10
        )
        output = res.stdout.strip()
        print(f"    Output: '{output}'")
        
        if res.returncode == 0 and output == "active":
            print("    -> JUDGEMENT: Firewall is ACTIVE (Score: 100)")
            return True, False
        else:
            print("    -> JUDGEMENT: Firewall is INACTIVE (Score: 0)")
            return False, True
    except FileNotFoundError:
        print("    -> ERROR: 'systemctl' command not found.")
        return False, False
    except Exception as e:
        print(f"    -> ERROR: {e}")
        return False, False

def _check_ssh_config(ssh_config: str) -> tuple[int, bool]:
    """Check SSH config. Returns (score_penalty, needs_fix)."""
    # ... extracted logic

Then call these from verify_security_logic().


75-82: Add timeout to subprocess calls.

The subprocess.run call for enabling UFW could hang. Add a timeout parameter.

     try:
         # Depends on execution environment, sudo might be required
-        subprocess.run(["sudo", "ufw", "enable"], check=True)
+        subprocess.run(["sudo", "ufw", "enable"], check=True, timeout=30)
         print("    -> ✅ Success: Firewall enabled.")
         return True
-    except subprocess.CalledProcessError as e:
+    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
         print(f"    -> ❌ Failed to enable firewall: {e}")
         return False
cortex/health/checks/disk.py (1)

4-6: Add docstrings and use _ for unused variable.

Per coding guidelines, docstrings are required for public APIs. Also, rename free to _ since it's unused.

 class DiskCheck(HealthCheck):
+    """Check disk space usage on root filesystem."""
+    
     def run(self) -> CheckResult:
-        total, used, free = shutil.disk_usage("/")
+        """
+        Check disk usage percentage and return health score.
+        
+        Returns:
+            CheckResult with score based on disk usage thresholds.
+        """
+        total, used, _ = shutil.disk_usage("/")
cortex/cli.py (1)

177-181: Consider wrapping HealthMonitor in try-except.

If any health check raises an unexpected exception, it could crash the CLI. While HealthMonitor.run_all has internal exception handling, wrapping the call adds defense-in-depth.

         from cortex.health.monitor import HealthMonitor
         
         self._print_status("🔍", "Running system health checks...")
-        monitor = HealthMonitor()
-        report = monitor.run_all()
+        try:
+            monitor = HealthMonitor()
+            report = monitor.run_all()
+        except Exception as e:
+            self._print_error(f"Health check failed: {e}")
+            return 1
cortex/health/checks/security.py (3)

5-6: Add docstrings for public class and method.

Per coding guidelines, docstrings are required for all public APIs.

 class SecurityCheck(HealthCheck):
+    """Check security configuration including firewall and SSH settings."""
+    
     def run(self) -> CheckResult:
+        """
+        Run security checks for firewall status and SSH configuration.
+        
+        Returns:
+            CheckResult with security score based on detected issues.
+        """
         score = 100

14-17: Add timeout to subprocess.run.

The subprocess call could hang if systemctl is unresponsive.

             res = subprocess.run(
                 ["systemctl", "is-active", "ufw"], 
-                capture_output=True, text=True
+                capture_output=True, text=True, timeout=10
             )

You'll also need to handle subprocess.TimeoutExpired:

-        except FileNotFoundError:
+        except (FileNotFoundError, subprocess.TimeoutExpired):
             pass # Environment without systemctl (e.g., Docker or non-systemd)

11-27: Consider extracting firewall check to reduce complexity.

Static analysis reports cognitive complexity of 16 (limit 15). While only marginally over, extracting the firewall check into a helper method would improve readability and make future security checks easier to add.

def _check_firewall(self) -> tuple[bool, str, str]:
    """Check if UFW firewall is active.
    
    Returns:
        Tuple of (is_active, issue_text, recommendation_text)
    """
    try:
        res = subprocess.run(
            ["systemctl", "is-active", "ufw"],
            capture_output=True, text=True, timeout=10
        )
        if res.returncode == 0 and res.stdout.strip() == "active":
            return True, None, None
    except (FileNotFoundError, subprocess.TimeoutExpired):
        pass
    return False, "Firewall Inactive", "Enable UFW Firewall"
tests/test_health_monitor.py (3)

32-60: Consider adding a test for the high-memory penalty path in PerformanceCheck

You cover the “no penalty” and “high load” scenarios, but never drive the used_percent > 80 branch. A small additional test would lock in behaviour for memory pressure and guard future changes, e.g. using a low MemAvailable to assert a reduced score and "High Memory" in details.


61-84: Optionally add coverage for SSH root-login penalty in SecurityCheck

Current tests validate UFW inactive/active flows but not the PermitRootLogin yes case that should subtract 50 points and add a "Root SSH Allowed" issue. A focused test that patches os.path.exists and open for sshd_config would tighten coverage on a security-sensitive branch.


110-135: HealthMonitor aggregation test is solid; you could also assert status/details if desired

The weighted-average calculation and result-count assertions verify the core aggregation logic and history-skipping behaviour. As a small enhancement, you could also assert that the per-check entries in report["results"] preserve the input name/category and that total_score is an int.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a1755c and bdfcccf.

📒 Files selected for processing (8)
  • cortex/cli.py (9 hunks)
  • cortex/health/checks/disk.py (1 hunks)
  • cortex/health/checks/performance.py (1 hunks)
  • cortex/health/checks/security.py (1 hunks)
  • cortex/health/checks/updates.py (1 hunks)
  • cortex/health/monitor.py (1 hunks)
  • scripts/verify_ubuntu_compatibility.py (1 hunks)
  • tests/test_health_monitor.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/health/checks/disk.py
  • scripts/verify_ubuntu_compatibility.py
  • tests/test_health_monitor.py
  • cortex/cli.py
  • cortex/health/monitor.py
  • cortex/health/checks/performance.py
  • cortex/health/checks/security.py
  • cortex/health/checks/updates.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_health_monitor.py
🧬 Code graph analysis (6)
cortex/health/checks/disk.py (3)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
cortex/health/checks/performance.py (1)
  • run (6-63)
cortex/health/checks/updates.py (1)
  • run (5-56)
cortex/cli.py (2)
cortex/first_run_wizard.py (1)
  • save_config (146-152)
cortex/health/monitor.py (2)
  • HealthMonitor (28-102)
  • run_all (52-87)
cortex/health/monitor.py (4)
cortex/health/checks/security.py (1)
  • SecurityCheck (5-57)
cortex/health/checks/updates.py (1)
  • UpdateCheck (4-56)
cortex/health/checks/performance.py (1)
  • PerformanceCheck (5-63)
cortex/health/checks/disk.py (1)
  • DiskCheck (4-33)
cortex/health/checks/performance.py (2)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
cortex/health/checks/disk.py (1)
  • run (5-33)
cortex/health/checks/security.py (3)
cortex/health/monitor.py (1)
  • CheckResult (12-20)
cortex/health/checks/disk.py (1)
  • run (5-33)
cortex/health/checks/updates.py (1)
  • run (5-56)
cortex/health/checks/updates.py (1)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
🪛 GitHub Check: SonarCloud Code Analysis
cortex/health/checks/disk.py

[warning] 6-6: Replace the unused local variable "free" with "_".

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52dKRWcWs7230WwT&open=AZsN52dKRWcWs7230WwT&pullRequest=292


[warning] 20-20: Fix this condition that always evaluates to false.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52dKRWcWs7230WwU&open=AZsN52dKRWcWs7230WwU&pullRequest=292

scripts/verify_ubuntu_compatibility.py

[warning] 62-62: Add replacement fields or use a normal string instead of an f-string.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52e4RWcWs7230WwY&open=AZsN52e4RWcWs7230WwY&pullRequest=292


[failure] 138-138: Refactor this function to reduce its Cognitive Complexity from 33 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52e5RWcWs7230WwZ&open=AZsN52e5RWcWs7230WwZ&pullRequest=292

tests/test_health_monitor.py

[warning] 59-59: Remove this commented out code.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52XKRWcWs7230WwS&open=AZsN52XKRWcWs7230WwS&pullRequest=292

cortex/cli.py

[warning] 175-175: Remove the unused function parameter "args".

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52d-RWcWs7230WwW&open=AZsN52d-RWcWs7230WwW&pullRequest=292


[warning] 666-666: Remove the unused local variable "health_parser".

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52d-RWcWs7230WwX&open=AZsN52d-RWcWs7230WwX&pullRequest=292

cortex/health/checks/security.py

[failure] 6-6: Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52dTRWcWs7230WwV&open=AZsN52dTRWcWs7230WwV&pullRequest=292

🔇 Additional comments (6)
cortex/health/checks/disk.py (1)

15-24: LGTM!

The threshold-based scoring logic is clear and correctly implements the specification. The static analysis warning about line 20 being always false is a false positive—the elif branch is correctly reachable when 80 < usage_percent <= 90.

cortex/cli.py (2)

174-222: LGTM! Health command is well-structured.

The health command implementation is clean:

  • Proper integration with HealthMonitor
  • Good visual formatting with color-coded scores
  • Clear display of factors and recommendations

The unused args parameter (flagged by static analysis) is acceptable for consistency with other CLI command method signatures, allowing future extension without signature changes.


664-667: Unused health_parser variable is acceptable.

Static analysis flags this as unused, but the pattern matches other subparsers (e.g., demo_parser, status_parser). This is fine—keeping the variable allows future addition of arguments to the health command without restructuring.

cortex/health/checks/security.py (1)

36-41: LGTM on SSH check logic.

Good use of "yes" in line.split() to avoid matching commented lines like #PermitRootLogin yes and to ensure exact word match rather than substring.

tests/test_health_monitor.py (2)

9-31: DiskCheck tests correctly exercise all threshold branches

The three scenarios (50%, 85%, 95% usage) map cleanly to OK/WARNING/CRITICAL and validate both score and status; mocking of disk_usage is scoped and appropriate.


85-109: UpdateCheck scoring test matches implementation and is robust

The constructed apt list --upgradable output and the expected 84 score align with the production scoring formula, and asserting "3 pending" in details ensures basic messaging is correct.

Comment on lines +5 to +63
class PerformanceCheck(HealthCheck):
def run(self) -> CheckResult:
score = 100
issues = []
rec = None

# 1. Load Average (1min)
try:
load1, _, _ = os.getloadavg()
cores = multiprocessing.cpu_count()
# Load ratio against core count
load_ratio = load1 / cores

if load_ratio > 1.0:
score -= 50
issues.append(f"High Load ({load1:.2f})")
rec = "Check top processes"
except Exception:
pass # Skip on Windows etc.

# 2. Memory Usage (Linux /proc/meminfo)
try:
with open('/proc/meminfo', 'r') as f:
meminfo = {}
for line in f:
parts = line.split(':')
if len(parts) == 2:
meminfo[parts[0].strip()] = int(parts[1].strip().split()[0])

if 'MemTotal' in meminfo and 'MemAvailable' in meminfo:
total = meminfo['MemTotal']
avail = meminfo['MemAvailable']
used_percent = ((total - avail) / total) * 100

if used_percent > 80:
penalty = int(used_percent - 80)
score -= penalty
issues.append(f"High Memory ({used_percent:.0f}%)")
except FileNotFoundError:
pass # Non-Linux systems

# Summary of results
status = "OK"
if score < 50:
status = "CRITICAL"
elif score < 90:
status = "WARNING"

details = ", ".join(issues) if issues else "Optimal"

return CheckResult(
name="System Load",
category="performance",
score=max(0, score),
status=status,
details=details,
recommendation=rec,
weight=0.20 # 20%
) No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add docstrings for PerformanceCheck and run to meet public-API guideline

PerformanceCheck is part of the public health-check surface but currently lacks docstrings. Adding brief class and method docstrings will align with the “docstrings required for all public APIs” guideline and clarify what this check measures.

-class PerformanceCheck(HealthCheck):
-    def run(self) -> CheckResult:
+class PerformanceCheck(HealthCheck):
+    """Health check for basic system performance (CPU load and memory usage)."""
+
+    def run(self) -> CheckResult:
+        """Compute a 0–100 performance score from load average and memory pressure."""
         score = 100
         issues = []
         rec = None
🤖 Prompt for AI Agents
In cortex/health/checks/performance.py around lines 5 to 63, the public class
PerformanceCheck and its run method lack docstrings; add a one- to two-line
class docstring describing that PerformanceCheck evaluates system performance
(load and memory) and its intended use in health checks, and add a short method
docstring for run describing what it measures, the return type (CheckResult),
and any important behavior (e.g., skipped checks on non-Linux/Windows handling);
keep both concise, present-tense, and follow the repository’s docstring style
(brief summary + returns note).

Comment on lines 119 to 126
with open(config_path, 'w') as f:
f.writelines(new_lines)
print(" -> ✅ Success: sshd_config updated.")

# Attempt to restart SSH service
print(" -> Restarting sshd service...")
subprocess.run(["sudo", "systemctl", "restart", "ssh"], check=False)
return True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

SSH restart may fail silently and leave config in inconsistent state.

If SSH restart fails (check=False), the user isn't clearly warned that the service may need manual restart. Also consider that if write succeeds but restart fails, the system could be in a partially-updated state.

             # Attempt to restart SSH service
             print("    -> Restarting sshd service...")
-            subprocess.run(["sudo", "systemctl", "restart", "ssh"], check=False)
+            result = subprocess.run(
+                ["sudo", "systemctl", "restart", "ssh"],
+                capture_output=True, text=True, timeout=30
+            )
+            if result.returncode != 0:
+                print("    -> ⚠️ SSH service restart failed. Please restart manually.")
             return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
with open(config_path, 'w') as f:
f.writelines(new_lines)
print(" -> ✅ Success: sshd_config updated.")
# Attempt to restart SSH service
print(" -> Restarting sshd service...")
subprocess.run(["sudo", "systemctl", "restart", "ssh"], check=False)
return True
with open(config_path, 'w') as f:
f.writelines(new_lines)
print(" -> ✅ Success: sshd_config updated.")
# Attempt to restart SSH service
print(" -> Restarting sshd service...")
result = subprocess.run(
["sudo", "systemctl", "restart", "ssh"],
capture_output=True, text=True, timeout=30
)
if result.returncode != 0:
print(" -> ⚠️ SSH service restart failed. Please restart manually.")
return True
🤖 Prompt for AI Agents
In scripts/verify_ubuntu_compatibility.py around lines 119 to 126, the script
currently restarts sshd with subprocess.run(..., check=False) which can fail
silently after writing the new sshd_config; change this to explicitly check the
restart result, create a backup of the original config before writing, attempt
the restart and if it fails restore the backup, print a clear error/warning
instructing manual intervention (or exit non-zero), and ensure you only return
True after a successful restart so the user is not misled.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
cortex/health/checks/security.py (3)

11-34: Firewall check implementation is solid.

The timeout, exact string match, and error handling for missing systemctl environments are well thought out. The scoring logic correctly sets score to 0 when the firewall is inactive per requirements.

Consider logging or narrowing the generic except Exception block (lines 28-29) to avoid silently suppressing unexpected errors, though the current approach prioritizes graceful degradation.


36-52: SSH root login check works but could be more precise.

The current logic on line 44 checks if "yes" appears anywhere in the split line. While this works for most well-formed configs, consider verifying that "yes" immediately follows PermitRootLogin:

-                        if line.startswith("PermitRootLogin") and "yes" in line.split():
+                        parts = line.split()
+                        if len(parts) >= 2 and parts[0] == "PermitRootLogin" and parts[1] == "yes":
                             score -= 50

This prevents false positives from malformed lines like PermitRootLogin no yes # comment.


6-66: Consider extracting helper methods to reduce cognitive complexity.

SonarCloud flagged this method with a cognitive complexity of 19 (threshold: 15). Extracting the firewall check (lines 11-34) and SSH check (lines 36-52) into separate helper methods would improve readability and maintainability:

def _check_firewall(self) -> tuple[int, list[str], list[str]]:
    """Check UFW firewall status."""
    # Lines 11-34 logic here
    # Return (score_delta, issues, recommendations)
    ...

def _check_ssh_root_login(self) -> tuple[int, list[str], list[str]]:
    """Check SSH root login configuration."""
    # Lines 36-52 logic here
    # Return (score_delta, issues, recommendations)
    ...

def run(self) -> CheckResult:
    """Execute security checks and return aggregated results."""
    score = 100
    issues = []
    recommendations = []
    
    fw_score, fw_issues, fw_recs = self._check_firewall()
    score += fw_score  # or score = fw_score if firewall sets absolute value
    issues.extend(fw_issues)
    recommendations.extend(fw_recs)
    
    ssh_score, ssh_issues, ssh_recs = self._check_ssh_root_login()
    score += ssh_score
    issues.extend(ssh_issues)
    recommendations.extend(ssh_recs)
    
    # Status determination and return...

This refactor would also make the logic easier to unit test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdfcccf and 95215f7.

📒 Files selected for processing (1)
  • cortex/health/checks/security.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/health/checks/security.py
🧬 Code graph analysis (1)
cortex/health/checks/security.py (1)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
🪛 GitHub Check: SonarCloud Code Analysis
cortex/health/checks/security.py

[failure] 6-6: Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52dTRWcWs7230WwV&open=AZsN52dTRWcWs7230WwV&pullRequest=292

🔇 Additional comments (3)
cortex/health/checks/security.py (3)

1-3: Imports look good.

The imports are appropriate for the functionality implemented.


54-56: Status derivation logic is correct.

The three-tier status mapping (OK, WARNING, CRITICAL) correctly reflects the score thresholds.


58-66: CheckResult construction looks correct.

The score clamping with max(0, score) properly handles negative scores, and the conditional logic for details and recommendations is sound.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cortex/health/checks/disk.py (1)

7-38: Disk check logic looks solid; consider adding error handling and confirm threshold boundaries

The implementation and scoring bands are clear and consistent; DiskCheck.run does what it claims. The main gap is robustness if shutil.disk_usage("/") were ever to raise (e.g., unexpected environment or permissions issue). Right now that would bubble up and potentially break the whole health run, unlike updates.run, which already guards its external interaction.

You could make the check more defensive by wrapping the disk usage call and returning a CRITICAL result on failure, for example:

-        # Use _ for unused variable (free space)
-        total, used, _ = shutil.disk_usage("/")
-        usage_percent = (used / total) * 100
-        
+        # Use _ for unused variable (free space)
+        try:
+            total, used, _ = shutil.disk_usage("/")
+            usage_percent = (used / total) * 100
+        except OSError as exc:
+            return CheckResult(
+                name="Disk Usage",
+                category="disk",
+                score=0,
+                status="CRITICAL",
+                details=f"Failed to read disk usage: {exc}",
+                recommendation="Check disk configuration and permissions",
+                weight=0.20,
+            )
+
         score = 100
         status = "OK"
         rec = None

Also, please double‑check that the boundary behavior is what you want: with > 90 / > 80, exactly 90% usage is treated as WARNING and exactly 80% as OK. If you intended 90% to be critical and 80% to be warning, switching to >= would make that explicit.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95215f7 and dff2092.

📒 Files selected for processing (3)
  • cortex/health/checks/disk.py (1 hunks)
  • cortex/health/checks/security.py (1 hunks)
  • cortex/health/checks/updates.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/health/checks/disk.py
  • cortex/health/checks/updates.py
  • cortex/health/checks/security.py
🧬 Code graph analysis (3)
cortex/health/checks/disk.py (2)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
cortex/health/checks/updates.py (1)
  • run (7-59)
cortex/health/checks/updates.py (4)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
cortex/health/checks/disk.py (1)
  • run (7-39)
cortex/health/checks/security.py (1)
  • run (8-45)
cortex/health/checks/performance.py (1)
  • run (6-63)
cortex/health/checks/security.py (1)
cortex/health/monitor.py (2)
  • HealthCheck (22-26)
  • CheckResult (12-20)
🪛 GitHub Check: SonarCloud Code Analysis
cortex/health/checks/disk.py

[warning] 26-26: Fix this condition that always evaluates to false.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN52dKRWcWs7230WwU&open=AZsN52dKRWcWs7230WwU&pullRequest=292

cortex/health/checks/security.py

[warning] 58-58: Remove this redundant Exception class; it derives from another which is already caught.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN7wSL5vWhCcwWCUyW&open=AZsN7wSL5vWhCcwWCUyW&pullRequest=292


[warning] 58-58: Remove this redundant Exception class; it derives from another which is already caught.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN7wSL5vWhCcwWCUyX&open=AZsN7wSL5vWhCcwWCUyX&pullRequest=292


[warning] 73-73: Remove this redundant Exception class; it derives from another which is already caught.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsN7wSL5vWhCcwWCUyY&open=AZsN7wSL5vWhCcwWCUyY&pullRequest=292

🔇 Additional comments (1)
cortex/health/checks/security.py (1)

5-45: SecurityCheck run implementation and public API shape look solid

The scoring, status mapping, and aggregation into CheckResult are straightforward and consistent with the 0–100 scale, and the class/method docstrings plus run(self) -> CheckResult signature satisfy the public API requirements. I don’t see functional or security issues in this segment.

As per coding guidelines, docstring coverage and type hints for the public API here look correct.

@hyaku0121 hyaku0121 changed the title Feature/health score 128 feat: Add system health monitoring module (#128) Dec 11, 2025
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System Health Score and Recommendations

1 participant