Back to skill

Security audit

Session Health Monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a local context-health utility, but it asks agents to persist session-derived facts into long-term memory without clear limits and includes unsafe local state handling.

Review this carefully before installing. Use it only if you are comfortable with agents writing summaries of session facts to local long-term memory, and avoid storing secrets, credentials, personal data, or untrusted instructions. Prefer changing the temp-state storage to a private per-user directory before enabling the statusline.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/statusline.sh:42
Finding
Predictable Temporary State File Allows Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/statusline.sh:42-67` **Vulnerability Type**: Predictable temporary-file creation and unsafe symlink following **Risk Level**: Medium ### Vulnerable Code ```bash # Session state file (ephemeral, in /tmp) session_id="${session_id:-unknown}" state_file="/tmp/session-health-${session_id}.json" # Load previous state prev_pct=0 compactions=0 if [[ -f "$state_file" ]]; then if command -v jq &>/dev/null; then prev_pct=$(jq -r '.last_pct // 0' "$state_file" 2>/dev/null) || prev_pct=0 compactions=$(jq -r '.compactions // 0' "$state_file" 2>/dev/null) || compactions=0 else prev_pct=$(grep -o '"last_pct":[0-9]*' "$state_file" | cut -d: -f2) || prev_pct=0 compactions=$(grep -o '"compactions":[0-9]*' "$state_file" | cut -d: -f2) || compactions=0 fi fi # Detect compaction: usage dropped by more than COMPACTION_DROP points if [[ "$prev_pct" -gt 0 ]] && [[ $((prev_pct - used_pct)) -ge "$COMPACTION_DROP" ]]; then compactions=$((compactions + 1)) fi # Save state cat > "$state_file" <<EOF {"last_pct":${used_pct},"compactions":${compactions},"updated":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"} EOF ``` The same predictable path is read by `scripts/context-check.sh:42-56`: ```bash state_file="/tmp/session-health-${session_id}.json" if [[ -f "$state_file" ]]; then if command -v jq &>/dev/null; then compactions=$(jq -r '.compactions // 0' "$state_file" 2>/dev/null) || compactions=0 # If no stdin data, use stored percentage if [[ -z "$used_pct" ]]; then used_pct=$(jq -r '.last_pct // empty' "$state_file" 2>/dev/null) || true fi else compactions=$(grep -o '"compactions":[0-9]*' "$state_file" | cut -d: -f2) || compactions=0 if [[ -z "$used_pct" ]]; then used_pct=$(grep -o '"last_pct":[0-9]*' "$state_file" | cut -d: -f2) || true fi fi fi ``` ### Technical Analysis The script stores session ...[truncated 2274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store state in a private per-user directory rather than directly in shared `/tmp`: ```bash state_dir="${XDG_RUNTIME_DIR:-$HOME/.cache}/session-health" umask 077 mkdir -p -- "$state_dir" chmod 700 -- "$state_dir" ``` 2. Restrict `session_id` to a safe character set or derive a fixed-length digest: ```bash safe_session_id=$(printf '%s' "$session_id" | sha256sum | cut -d' ' -f1) state_file="$state_dir/${safe_session_id}.json" ``` 3. Reject existing symbolic links and non-regular files. Verify that existing state is owned by the current user before reading it. 4. Write through a securely created temporary file and atomically rename it: ```bash tmp_file=$(mktemp "$state_dir/.state.XXXXXX") printf '{"last_pct":%s,"compactions":%s,"updated":"%s"}\n' \ "$used_pct" "$compactions" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$tmp_file" chmod 600 "$tmp_file" mv -f -- "$tmp_file" "$state_file" ``` 5. Validate loaded fields as bounded integers before arithmetic or status decisions. Reject malformed, negative, or unreasonable percentages and compaction counts. ]]>

T02 · Agent Memory Poisoning

Warning
Location
scripts/snapshot.sh:25
Finding
Session-Derived Content Can Be Persisted into Long-Term Agent Memory Without Trust Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/snapshot.sh:25-35, 58-68`; workflow instruction at `SKILL.md:89-94` **Vulnerability Type**: Persistent agent-memory poisoning **Risk Level**: Medium ### Vulnerable Code The Skill instructs the agent to persist facts extracted from the current session: ```markdown **When context reaches YELLOW or above, the agent SHOULD:** 1. Extract 3-5 key facts from the current session (decisions made, files changed, blockers found) 2. Write them to `memory/YYYY-MM-DD.md` using `scripts/snapshot.sh` 3. Include any unfinished work or next steps 4. Do this BEFORE the session ends or context is compacted ``` `snapshot.sh` accepts arbitrary argument or standard-input content: ```bash # Collect facts from args or stdin facts=() if [[ "${1:-}" == "-" ]]; then while IFS= read -r line; do [[ -n "$line" ]] && facts+=("$line") done else for arg in "$@"; do [[ -n "$arg" ]] && facts+=("$arg") done fi ``` The accepted content is appended verbatim to a persistent memory file: ```bash # Append snapshot { echo "" echo "## Pre-Compaction Snapshot ($time_now)" for fact in "${new_facts[@]}"; do echo "- $fact" done } >> "$daily_file" echo "Saved ${#new_facts[@]} fact(s) to $daily_file" ``` ### Technical Analysis The workflow crosses a trust boundary by extracting material from the current session and storing it in long-term agent memory. Current-session content may include attacker-controlled user messages, retrieved documents, tool output, or other untrusted text. The implementation records selected facts verbatim and does not: - Record the source or trust level of each fact. - Distinguish descriptive data from imperative instructions. - Filter instruction-like content. - Escape or structurally encode Markdown. - Require user confirmation before persistence. - Mark loaded memory as untrusted historical data. If an attacker causes imperative text to be selected as ...[truncated 1862 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Persist only agent-authored summaries rather than verbatim session, document, or tool-output content. 2. Add provenance and trust metadata to every record, including: - Source type. - Source identifier. - Whether the content originated from a user or external resource. - Timestamp. - Confirmation status. 3. Use a validated structured format such as JSON instead of free-form Markdown. Keep historical observations separate from operational instructions. 4. Reject or quarantine imperative content, executable snippets, role-changing language, and text that attempts to direct future agent behavior. 5. Require explicit user confirmation before storing facts derived from untrusted sources. 6. Define a mandatory memory-consumption rule stating that persisted entries are untrusted historical data and must never override current system, developer, user, or Skill instructions. 7. Consider maintaining separate files for trusted user-approved decisions and untrusted contextual notes. Only the trusted store should be eligible to guide future actions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code does implement part of the declared theme: context-window health monitoring with threshold-based status evaluation. However, the declared description specifically claims additional capabilities—Telegram warnings, pre-compaction snapshots, and memory rotation—that are absent from this code chunk. The script only parses context usage data, optionally consults a temp state file for compaction count and last percentage, emits status output, and sets exit codes. Because the stated purpose includes substantial behaviors not present in the supplied code, the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a broader monitoring and notification tool with Telegram warnings and pre-compaction snapshots. The supplied code only performs one maintenance task: rotating/archive daily memory files older than KEEP_DAYS from the memory directory into an archive folder. It does not monitor context window health, inspect thresholds, send messages, or create snapshots tied to compaction events. While 'memory rotation' is accurately represented, the overall description materially overstates the implemented behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is about context window health monitoring features for OpenClaw agents, specifically mentioning Telegram alerts, pre-compaction snapshots, and memory rotation. The supplied code does something materially different: it is an installer that sets up a Claude statusline script by copying a file into ~/.claude and editing ~/.claude/settings.local.json with jq. This is a local configuration/install action, not the described monitoring and alerting behavior. While a statusline could be related to health display, the stated core capabilities are not present in this chunk, and the code accesses/modifies local Claude config resources that are not disclosed by the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The implemented code is a simple snapshot logger, not a health monitoring system. It does include a pre-compaction snapshot-style write to a memory file, which partially matches the description, but the major advertised capabilities are absent: there is no monitoring of context window health, no thresholds, no Telegram integration, and no memory rotation behavior. The primary purpose of the code is local persistence of supplied facts with basic deduplication, which is materially narrower and different from the declared multi-feature monitoring/alerting description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code does implement part of the declared theme: it monitors context window usage and detects likely compactions based on drops in usage percentage. However, the declared description specifically promises threshold warnings via Telegram, pre-compaction snapshots, and memory rotation. None of those capabilities are present. Instead, the script is a local CLI/statusline utility that reads JSON from stdin, persists minimal per-session state in /tmp, and prints a color-coded status string. This is a materially narrower and different behavior than the declared description, so it should be flagged as a mismatch.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Reset compaction state
```bash
rm /tmp/session-health-*.json
```

### Agent not appending footer
Confidence
87% confidence
Finding
The troubleshooting command 'rm /tmp/session-health-*.json' performs wildcard deletion in a shared temporary directory. Even though constrained by a prefix, using rm on /tmp patterns can delete unintended files matching that glob, and in some environments an agent following documentation literally could remove another user's or process's files without confirmation.

Ssd 3

Medium
Confidence
93% confidence
Finding
The snapshot protocol instructs the agent to preserve key facts, decisions, blockers, unfinished work, and even user preferences into daily memory files before compaction. Persisting model context to local files can unintentionally store sensitive data, credentials, internal paths, proprietary information, or personal preferences beyond the original session boundary, increasing exposure and retention risk.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger condition 'when the agent detects it has accumulated significant context' is overly broad and subjective, which can cause excessive or inappropriate snapshotting of session state. In a memory-preservation skill, vague triggers increase the chance that sensitive information, user preferences, or transient secrets get written to disk more often than intended.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# macOS
brew install jq
# Linux
sudo apt-get install jq
```

### Reset compaction state
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &>/dev/null; then
    echo "Error: jq is required but not installed."
    echo "  macOS: brew install jq"
    echo "  Linux: sudo apt-get install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
session_id="${session_id:-unknown}"
state_file="/tmp/session-health-${session_id}.json"

# Load previous state
prev_pct=0
compactions=0
if [[ -f "$state_file" ]]; then
Confidence
88% confidence
Finding
The script persists per-session state in a predictable file under /tmp using a session_id derived from input without sanitization or secure file-creation semantics. On multi-user systems, this can enable symlink or file clobbering attacks, state spoofing, or cross-session interference if an attacker can pre-create or manipulate the target path, especially because the script later reads and overwrites that file.

Static analysis

No suspicious patterns detected.