Skip to content

t1398.5: Rewrite memory pressure monitor#2884

Merged
alex-solovyev merged 1 commit intomainfrom
feature/memory-pressure-monitor-rewrite
Mar 5, 2026
Merged

t1398.5: Rewrite memory pressure monitor#2884
alex-solovyev merged 1 commit intomainfrom
feature/memory-pressure-monitor-rewrite

Conversation

@alex-solovyev
Copy link
Collaborator

Summary

  • Rewrites memory pressure monitor to monitor process-level signals (individual RSS, runtime, process count) instead of kern.memorystatus_level as the primary signal
  • Addresses root cause of March 3 kernel panic: aidevops processes (ShellCheck at 5.7 GB, zombie pulses, session accumulation) — not generic OS memory pressure
  • Incorporates valid concepts from declined PR feat: add memory pressure monitor with launchd integration #2792 (launchd integration, notifications, cooldown, CLI) with correct thresholds and security fixes

What Changed

New file: .agents/scripts/memory-pressure-monitor.sh

Signal Threshold Action
Per-process RSS warn 2 GB, critical 4 GB Notify per-category with cooldown
ShellCheck runtime > 10 min Warning notification
Other tool runtime > 30 min Warning notification
Interactive sessions >= 5 concurrent Warning notification
Aggregate aidevops RSS >= 8 GB total Warning notification
kern.memorystatus_level Logged only Secondary/informational — NOT used for alerts

CLI: --check (launchd), --status, --daemon, --install/--uninstall, --help

Security fixes vs PR #2792:

  • _validate_int() prevents command injection via $(( )) arithmetic expansion
  • AppleScript osascript inputs sanitised (quotes/backslashes escaped)
  • All environment variable inputs validated before use
  • No XML injection risk in plist generation

New file: tests/test-memory-pressure-monitor.sh — 28 unit tests covering:

  • Configuration validation (valid, non-numeric, below-minimum, leading zeros, injection)
  • Duration formatting (seconds, minutes, hours, days, zero)
  • Cooldown logic (no file, set+check, clear, invalid content)
  • Process age parsing (self, nonexistent PID)
  • OS memory info format and values
  • CLI commands (help, status, check, unknown)
  • Security (env var injection, notification disable)

Testing

Results: 28 passed (total: 28)

ShellCheck: zero violations on both files.

Closes #2878

Rewrite memory pressure monitor to monitor the right signals:
- Per-process RSS (warn 2GB, critical 4GB) instead of kern.memorystatus_level
- Process runtime limits (10min shellcheck, 30min other tools)
- Interactive session count (warn >= 5)
- Aggregate aidevops RSS (warn >= 8GB)

kern.memorystatus_level retained as secondary/informational signal only.
Fixes security issues from declined PR #2792 (command injection via
arithmetic eval, AppleScript injection, XML injection in plist).

Includes 28 unit tests covering validation, cooldown, CLI, and security.

Closes #2878
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 5, 2026

Warning

Rate limit exceeded

@alex-solovyev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 48 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.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ab73ba6-7bff-4b2b-8f3f-c39a13a68037

📥 Commits

Reviewing files that changed from the base of the PR and between 088f783 and 9f641b9.

📒 Files selected for processing (2)
  • .agents/scripts/memory-pressure-monitor.sh
  • tests/test-memory-pressure-monitor.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/memory-pressure-monitor-rewrite

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.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a re-engineered memory pressure monitor that prioritizes process-level metrics over OS-level signals to prevent system instability caused by specific aidevops processes. This new monitor provides granular control and alerts based on individual process RSS, runtime, and interactive session counts, while also integrating robust security measures and a comprehensive testing framework.

Highlights

  • Shifted Monitoring Focus: The memory pressure monitor was rewritten to prioritize process-level signals (individual RSS, runtime, process count) over the less reliable kern.memorystatus_level OS signal.
  • Addressed Kernel Panic Root Cause: The new monitor specifically targets aidevops processes (e.g., ShellCheck, language servers) identified as the cause of a previous kernel panic, rather than generic OS memory pressure.
  • Integrated Advanced Features: Valid concepts from a previously declined PR (feat: add memory pressure monitor with launchd integration #2792) were incorporated, including launchd integration, desktop notifications, cooldown mechanisms to prevent spam, and a comprehensive command-line interface.
  • Enhanced Security: Critical security fixes were implemented to prevent command injection via arithmetic expansion (_validate_int()), AppleScript injection through osascript input sanitization, and validation of all environment variable inputs.
  • Comprehensive Testing: A new unit test file with 28 tests was added, covering configuration validation, duration formatting, cooldown logic, process age parsing, OS memory info, CLI commands, and security aspects, ensuring robustness.
Changelog
  • .agents/scripts/memory-pressure-monitor.sh
    • Implemented a new memory pressure monitor script.
    • Defined process-level thresholds for RSS, runtime, and session count.
    • Added OS-level memory monitoring for informational purposes.
    • Included notification, cooldown, and logging functionalities.
    • Provided CLI commands for check, status, daemon, install, uninstall, and help.
    • Incorporated security measures against command and AppleScript injection.
  • tests/test-memory-pressure-monitor.sh
    • Added a new unit test file for the memory pressure monitor.
    • Covered configuration validation, duration formatting, cooldown logic, process age parsing, OS memory info, interactive session counting, CLI commands, and security aspects.
Activity
  • A new memory pressure monitor script has been added to the .agents/scripts directory.
  • A comprehensive suite of 28 unit tests has been introduced in tests/test-memory-pressure-monitor.sh to validate the new monitor's functionality and security.
  • The pull request description details the motivation, changes, and testing results, indicating a thorough development process.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Mar 5, 2026

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 107 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Thu Mar 5 03:09:09 UTC 2026: Code review monitoring started
Thu Mar 5 03:09:09 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 107

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 107
  • VULNERABILITIES: 0

Generated on: Thu Mar 5 03:09:12 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 5, 2026

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a well-designed and robust memory pressure monitor script with comprehensive unit tests, focusing on process-level signals. While it includes some security improvements, critical vulnerabilities persist. The notification logic is susceptible to AppleScript injection, which can lead to arbitrary command execution, and improper validation of environment variables in arithmetic contexts could allow for command injection. Additionally, there are suggestions to enhance the efficiency of process parsing loops and align with project conventions on error handling. Addressing these security concerns and incorporating the suggested code improvements is crucial before merging.

Comment on lines +160 to +164
safe_title="${safe_title//\"/\\\"}"
local safe_message="${message//\\/\\\\}"
safe_message="${safe_message//\"/\\\"}"
osascript -e "display notification \"${safe_message}\" with title \"${safe_title}\"" 2>/dev/null || true
return 0

Choose a reason for hiding this comment

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

security-high high

The notify function attempts to sanitize the title and message arguments before passing them to osascript by escaping backslashes and double quotes. However, this escaping is insufficient to prevent an attacker from breaking out of the AppleScript string literal. By providing a string containing "; do shell script "..., an attacker can execute arbitrary shell commands with the privileges of the user running the script. Since the monitor includes process names in the notification message (e.g., line 419), and process names can be controlled by any user on the system, this represents a significant security risk.

To remediate this, consider passing the AppleScript via stdin to osascript or using a more robust escaping mechanism for AppleScript string literals.

AGGREGATE_RSS_WARN_MB="${AGGREGATE_RSS_WARN_MB:-8192}" # 8 GB total

# Notification
readonly COOLDOWN_SECS="${MEMORY_COOLDOWN_SECS:-300}"

Choose a reason for hiding this comment

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

security-medium medium

The environment variable MEMORY_COOLDOWN_SECS is assigned to the COOLDOWN_SECS variable without any validation. This variable is later used in an arithmetic context within a [[ ... ]] block (line 186). In bash, arithmetic evaluation of variables can lead to arbitrary command execution if the variable contains a specially crafted string, such as an array index with a command substitution (e.g., a[$(id>out)0]).

You should use the _validate_int function to ensure this variable is a valid integer before use, similar to how other numeric configuration variables are handled.

local cooldown_file="${STATE_DIR}/memory-pressure-${category}.cooldown"
if [[ -f "${cooldown_file}" ]]; then
local last_notify
last_notify="$(cat "${cooldown_file}" 2>/dev/null || echo 0)"

Choose a reason for hiding this comment

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

medium

You are suppressing stderr from cat after already checking if the file exists with [[ -f ... ]]. This is contrary to the project's general rules, which advise against blanket error suppression with 2>/dev/null. Suppressing the error here could mask issues like file permission problems. Removing 2>/dev/null will allow such errors to be logged, aiding in debugging, while the || echo 0 fallback will still handle the failure case gracefully.

Suggested change
last_notify="$(cat "${cooldown_file}" 2>/dev/null || echo 0)"
last_notify="$(cat "${cooldown_file}" || echo 0)"
References
  1. Avoid using 2>/dev/null to suppress errors on file operations if the file's existence has already been verified by a preceding check (e.g., [[ -f "$file" ]] or an early return). This practice is redundant for 'file not found' errors and can mask other important issues like permissions problems.

Comment on lines +300 to +320
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local pid rss_kb cmd
pid=$(echo "$line" | awk '{print $1}')
rss_kb=$(echo "$line" | awk '{print $2}')
cmd=$(echo "$line" | cut -d' ' -f3-)

# Validate PID and RSS are numeric
[[ "$pid" =~ ^[0-9]+$ ]] || continue
[[ "$rss_kb" =~ ^[0-9]+$ ]] || rss_kb=0

local rss_mb=$((rss_kb / 1024))
local runtime
runtime=$(_get_process_age "$pid")

# Extract short command name
local cmd_name
cmd_name=$(basename "$(echo "$cmd" | awk '{print $1}')" 2>/dev/null || echo "unknown")

printf '%s|%s|%s|%s|%s\n' "$pid" "$rss_mb" "$runtime" "$cmd_name" "$cmd"
done <<<"$ps_output"

Choose a reason for hiding this comment

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

medium

The while loop for parsing ps output is spawning multiple subshells (echo, awk, cut) for each process, which is inefficient. You can achieve the same result more efficiently by using the read built-in to parse the line and shell parameter expansion to extract the command name. This avoids the overhead of creating new processes in the loop.

Suggested change
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local pid rss_kb cmd
pid=$(echo "$line" | awk '{print $1}')
rss_kb=$(echo "$line" | awk '{print $2}')
cmd=$(echo "$line" | cut -d' ' -f3-)
# Validate PID and RSS are numeric
[[ "$pid" =~ ^[0-9]+$ ]] || continue
[[ "$rss_kb" =~ ^[0-9]+$ ]] || rss_kb=0
local rss_mb=$((rss_kb / 1024))
local runtime
runtime=$(_get_process_age "$pid")
# Extract short command name
local cmd_name
cmd_name=$(basename "$(echo "$cmd" | awk '{print $1}')" 2>/dev/null || echo "unknown")
printf '%s|%s|%s|%s|%s\n' "$pid" "$rss_mb" "$runtime" "$cmd_name" "$cmd"
done <<<"$ps_output"
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local pid rss_kb cmd
read -r pid rss_kb cmd <<<"$line"
# Validate PID and RSS are numeric
[[ "$pid" =~ ^[0-9]+$ ]] || continue
[[ "$rss_kb" =~ ^[0-9]+$ ]] || rss_kb=0
local rss_mb=$((rss_kb / 1024))
local runtime
runtime=$(_get_process_age "$pid")
# Extract short command name
local cmd_path=${cmd%% *}
local cmd_name
cmd_name=$(basename "$cmd_path" 2>/dev/null || echo "unknown")
printf '%s|%s|%s|%s|%s\n' "$pid" "$rss_mb" "$runtime" "$cmd_name" "$cmd"
done <<<"$ps_output"

Comment on lines +331 to +339
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local tty
tty=$(echo "$line" | awk '{print $2}')
# Interactive sessions have a TTY (not "??" on macOS or "?" on Linux)
if [[ "$tty" != "??" && "$tty" != "?" ]]; then
count=$((count + 1))
fi
done <<<"$ps_output"

Choose a reason for hiding this comment

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

medium

Similar to the process collection loop, this while loop for counting interactive sessions can be made more efficient. It currently uses echo and awk in a subshell for each line. You can parse the line directly with read to improve performance and readability.

Suggested change
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local tty
tty=$(echo "$line" | awk '{print $2}')
# Interactive sessions have a TTY (not "??" on macOS or "?" on Linux)
if [[ "$tty" != "??" && "$tty" != "?" ]]; then
count=$((count + 1))
fi
done <<<"$ps_output"
while read -r _ tty _; do
# Interactive sessions have a TTY (not "??" on macOS or "?" on Linux)
if [[ "$tty" != "??" && "$tty" != "?" ]]; then
count=$((count + 1))
fi
done <<<"$ps_output"

@alex-solovyev alex-solovyev merged commit 9c20fdc into main Mar 5, 2026
27 of 28 checks passed
@alex-solovyev alex-solovyev deleted the feature/memory-pressure-monitor-rewrite branch March 5, 2026 03:40
marcusquinn added a commit that referenced this pull request Mar 5, 2026
…2884

- HIGH: Fix AppleScript injection in notify() — pass script via stdin
  with 'on run argv' instead of -e string interpolation, preventing
  breakout via crafted process names
- MEDIUM: Validate MEMORY_COOLDOWN_SECS and DAEMON_INTERVAL with
  _validate_int() to prevent arithmetic expansion injection
- MEDIUM: Remove redundant 2>/dev/null on cat after file existence check
  to surface permission errors for debugging
- MEDIUM: Optimize _collect_monitored_processes() loop — use read builtin
  and parameter expansion instead of echo|awk|cut subshells per line
- MEDIUM: Optimize _count_interactive_sessions() loop — use read builtin
  instead of echo|awk subshell per line

Closes #2899
marcusquinn added a commit that referenced this pull request Mar 5, 2026
…2884

- HIGH: Fix AppleScript injection in notify() — pass script via stdin
  with 'on run argv' instead of -e string interpolation, preventing
  breakout via crafted process names
- MEDIUM: Validate MEMORY_COOLDOWN_SECS and DAEMON_INTERVAL with
  _validate_int() to prevent arithmetic expansion injection
- MEDIUM: Remove redundant 2>/dev/null on cat after file existence check
  to surface permission errors for debugging
- MEDIUM: Optimize _collect_monitored_processes() loop — use read builtin
  and parameter expansion instead of echo|awk|cut subshells per line
- MEDIUM: Optimize _count_interactive_sessions() loop — use read builtin
  instead of echo|awk subshell per line

Closes #2899
marcusquinn added a commit that referenced this pull request Mar 5, 2026
…2884 (#2930)

- HIGH: Fix AppleScript injection in notify() — pass script via stdin
  with 'on run argv' instead of -e string interpolation, preventing
  breakout via crafted process names
- MEDIUM: Validate MEMORY_COOLDOWN_SECS and DAEMON_INTERVAL with
  _validate_int() to prevent arithmetic expansion injection
- MEDIUM: Remove redundant 2>/dev/null on cat after file existence check
  to surface permission errors for debugging
- MEDIUM: Optimize _collect_monitored_processes() loop — use read builtin
  and parameter expansion instead of echo|awk|cut subshells per line
- MEDIUM: Optimize _count_interactive_sessions() loop — use read builtin
  instead of echo|awk subshell per line

Closes #2899
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.

t1398.5: Rewrite memory pressure monitor

1 participant