Back to skill

Security audit

Session State Watch

Security checks for vulnerabilities and agentic risk

Overview

This skill is review-worthy because it persistently teaches the agent to read background-written session state, and its helper can silently truncate that state or leave a background watcher running.

Install only if you trust every process that can write SESSION-STATE.md and are comfortable with a persistent AGENTS.md rule affecting future agent responses. Before use, harden it by labeling imported state as untrusted data, removing or gating auto-truncation with backups and locking, avoiding the nohup daemon unless explicitly needed, and replacing broad pkill stopping with PID-based control.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check_session_state.sh:21
Finding
Predictable Temporary File Allows Symlink-Based File Clobbering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_session_state.sh`, lines 21-50 **Vulnerability Type**: Predictable temporary file and unsafe file replacement **Risk Level**: Medium ### Vulnerable Code ```bash truncate_if_needed() { local line_count line_count=$(wc -l < "$SESSION_STATE" 2>/dev/null || echo "0") if [ "$line_count" -gt "$MAX_LINES" ]; then echo "⚠️ SESSION-STATE.md has $line_count lines (>$MAX_LINES), truncating to last $KEEP_LINES lines..." local truncated_count=$((line_count - KEEP_LINES)) echo " (Truncating $truncated_count lines of history)" tail -"$KEEP_LINES" "$SESSION_STATE" > "${SESSION_STATE}.tmp" { echo "---" echo "## 📋 历史截断通知" echo "" echo "**时间**: $(date -Iseconds)" echo "**原因**: 文件超过 ${MAX_LINES} 行(当前 ${line_count} 行)" echo "**操作**: 保留最近 ${KEEP_LINES} 行,截断 ${truncated_count} 行历史" echo "**建议**: 重要内容已归档至 SESSION-STATE-history.md" echo "" echo "---" echo "" cat "${SESSION_STATE}.tmp" } > "$SESSION_STATE" rm -f "${SESSION_STATE}.tmp" echo "✅ Truncation complete. New size: $(wc -l < "$SESSION_STATE") lines" fi } ``` ### Technical Analysis The script uses the fixed path `${SESSION_STATE}.tmp` as a temporary file. Shell output redirection creates or truncates that path without exclusive creation, ownership verification, or symlink rejection. If the script is executed with elevated privileges and an attacker can create entries in the workspace directory, the attacker can pre-create `SESSION-STATE.md.tmp` as a symbolic link. The redirection used by `tail` will follow that link and truncate or overwrite the linked target with the last 1,000 lines of the session-state file. The script hardcodes the workspace under `/root/.openclaw/workspace`, increasing the potential ...[truncated 1197 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create temporary files with `mktemp` rather than a predictable filename. - Set `umask 077` before creating temporary or tracker files. - Create the temporary file in the same directory as the destination so that the final rename remains atomic. - Use a cleanup trap to remove the temporary file on interruption. - Validate that the destination and workspace directory are not attacker-controlled. - Replace the original file using an atomic `mv` only after all temporary output has been written successfully. Example hardening pattern: ```bash umask 077 tmp_file=$(mktemp "${SESSION_STATE}.tmp.XXXXXX") trap 'rm -f -- "$tmp_file"' EXIT tail -n "$KEEP_LINES" -- "$SESSION_STATE" > "$tmp_file" final_file=$(mktemp "${SESSION_STATE}.new.XXXXXX") trap 'rm -f -- "$tmp_file" "$final_file"' EXIT { printf '%s\n' "---" printf '%s\n' "## History truncation notice" printf '%s\n\n' "**Time**: $(date -Iseconds)" cat -- "$tmp_file" } > "$final_file" mv -f -- "$final_file" "$SESSION_STATE" rm -f -- "$tmp_file" trap - EXIT ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/check_session_state.sh:21
Finding
Automatic State Truncation Causes Data Loss and Falsely Claims Archival<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_session_state.sh`, lines 21-50 **Vulnerability Type**: Destructive state management and inaccurate recovery information **Risk Level**: Medium ### Vulnerable Code ```bash truncate_if_needed() { local line_count line_count=$(wc -l < "$SESSION_STATE" 2>/dev/null || echo "0") if [ "$line_count" -gt "$MAX_LINES" ]; then echo "⚠️ SESSION-STATE.md has $line_count lines (>$MAX_LINES), truncating to last $KEEP_LINES lines..." local truncated_count=$((line_count - KEEP_LINES)) echo " (Truncating $truncated_count lines of history)" tail -"$KEEP_LINES" "$SESSION_STATE" > "${SESSION_STATE}.tmp" { echo "---" echo "## 📋 历史截断通知" echo "" echo "**时间**: $(date -Iseconds)" echo "**原因**: 文件超过 ${MAX_LINES} 行(当前 ${line_count} 行)" echo "**操作**: 保留最近 ${KEEP_LINES} 行,截断 ${truncated_count} 行历史" echo "**建议**: 重要内容已归档至 SESSION-STATE-history.md" echo "" echo "---" echo "" cat "${SESSION_STATE}.tmp" } > "$SESSION_STATE" rm -f "${SESSION_STATE}.tmp" echo "✅ Truncation complete. New size: $(wc -l < "$SESSION_STATE") lines" fi } ``` The behavior is also represented in `SKILL.md`, lines 200-201: ```markdown **Q: SESSION-STATE.md keeps growing — how do I manage it?** A: The script auto-truncates when the file exceeds 2000 lines, keeping the last 1000 lines with a truncation notice at top. Check the `MAX_LINES` and `KEEP_LINES` variables in `check_session_state.sh` to adjust thresholds. ``` ### Technical Analysis Whenever the state file exceeds 2,000 lines, a normal change check automatically replaces it with only the last 1,000 lines plus a notice. No archival operation is performed before the overwrite. The generated notice states that important content was archived ...[truncated 1302 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not delete state automatically during a normal read operation unless the user has explicitly enabled retention management. - Archive removed content to a real history file before replacing the active file. - Verify that the archive was successfully written and synchronized before truncating the original. - Remove the archival claim unless archival is actually implemented. - Use file locking to prevent concurrent writers from being lost during rotation. - Perform rotation through secure temporary files and an atomic rename. - Consider size-based log rotation with numbered, permission-restricted archives. - Preserve backups and report a hard failure rather than continuing if archival or replacement fails. ]]>

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:59
Finding
Persistent Pre-Response State Ingestion Enables Prompt Injection from Background Writers<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 59-95 **Vulnerability Type**: Untrusted content ingestion into agent context **Risk Level**: High ### Vulnerable Instructions ```markdown ### 3. Add Detection Rule to AGENTS.md Add this section to your `~/.openclaw/workspace/AGENTS.md`: ```markdown ## 🔔 Session State Change Detection (L3 Active Awareness) **Problem**: Cron tasks (dream learning 04:30, post-market learning 15:05) modify `SESSION-STATE.md`, but the main session doesn't automatically know about the changes. **Solution**: Before substantive answers (not simple acknowledgments), check if `SESSION-STATE.md` has been modified: \```bash # Check if SESSION-STATE.md is newer than tracker MTIME=$(stat -c %Y /root/.openclaw/workspace/SESSION-STATE.md 2>/dev/null || echo "0") TRACKER_JSON="/root/.openclaw/workspace/data/.session_state_tracker.json" TRACKER=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('last_known_mtime', 0))" < "$TRACKER_JSON" 2>/dev/null || echo "0") if [ "$MTIME" -gt "$TRACKER" ] 2>/dev/null; then echo "SESSION-STATE.md has been updated!" # Read the file and summarize changes fi \``` **Implementation**: 1. **Tracker file**: `data/.session_state_tracker.json` stores last-known mtime 2. **Check on answer**: Before substantive responses, run the check above 3. **If changed**: Read `SESSION-STATE.md`, summarize new content, update tracker 4. **Update tracker**: After reading, update `last_known_mtime` in tracker to current mtime ``` ``` ### Technical Analysis The skill directs the user to add a persistent rule to `AGENTS.md`. That rule makes the agent read and summarize newly written `SESSION-STATE.md` content before substantive responses. The state file is explicitly intended to receive content from isolated cron jobs and background tasks. The instructions do not establish an authenticated writer set, distinguish trusted data from instructions, delimit imported content, or tell the ...[truncated 1625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Explicitly classify all `SESSION-STATE.md` content as untrusted data. - Add a persistent instruction that commands, role changes, tool requests, and policy overrides found in the state file must never be followed. - Parse a constrained structured format rather than importing arbitrary Markdown into the agent context. - Authenticate approved writers or assign each producer a protected, dedicated input file. - Validate file ownership and permissions before reading updates. - Use an allowlist of accepted record types and reject unknown fields. - Present imported data inside clear delimiters and label the source. - Summarize state through a non-agent parser where possible rather than exposing raw text. - Require user confirmation before acting on requests discovered in background-generated state. - Apply least privilege so background tasks that do not need to write agent state cannot modify the file. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior materially exceeds the declared purpose of simple change detection: it can rewrite SESSION-STATE.md, run a persistent watcher/daemon, kill processes, and write logs under /tmp. This mismatch is dangerous because users or orchestrators may approve the skill expecting passive monitoring, while it actually gains state-modifying and process-control behavior that could disrupt sessions or conceal changes.

Ae1

High
Category
analysis-evasion
Content
| `SKILL.md` | This documentation |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
94% confidence
Finding
A skill framed as detection-only should not silently modify the monitored state file by truncating and rewriting it. Altering SESSION-STATE.md can destroy audit history, hide prior entries, and create integrity problems for any automation that relies on the file as a durable communication channel.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill documents file-writing behavior, tracker creation, optional daemon logging, and monitored-file maintenance, but declares no explicit tool scope or allowed-tools. In an agent-skill ecosystem, missing permission boundaries increases the chance that a caller or agent invokes broader write capabilities than users expect, especially because the skill’s stated purpose sounds read/notify-oriented.

Session Persistence

Medium
Category
Rogue Agent
Content
## When to Use

Use this skill when:
- You have cron tasks that write results to `SESSION-STATE.md`
- You want the main agent session to automatically detect and respond to background task outputs
- You need to sync state between isolated cron sessions and the main session
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Description-Behavior Mismatch

Medium
Confidence
85% confidence
Finding
The documentation markets the approach as lightweight and not requiring external daemons, yet also provides a watch/daemon mode. That inconsistency can mislead users and policy engines about persistence and runtime behavior, leading to unintended background execution and reduced visibility into what the skill is doing over time.

Session Persistence

Medium
Category
Rogue Agent
Content
bash scripts/check_session_state.sh --watch

# Background daemon
nohup bash scripts/check_session_state.sh --watch --daemon > /tmp/session-state-watch.log 2>&1 &

# Stop daemon
bash scripts/check_session_state.sh --stop-daemon
Confidence
78% confidence
Finding
The documented use of nohup to launch a background daemon introduces persistence beyond the main interactive session. Persistent background processes can continue monitoring, logging, or modifying state after the user’s immediate task ends, which is materially more risky in an agent setting because it can outlive user awareness and oversight.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
Claiming 'No daemon process' while also documenting daemon mode is a direct contradiction that obscures the persistence model of the skill. In security-sensitive agent environments, hidden or poorly disclosed persistence increases the risk of unnoticed background activity, stale watchers, and confusing operational behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
#!/usr/bin/env python3
"""
Example: How to write SESSION-STATE.md updates from cron tasks.

This pattern allows cron tasks to write results to SESSION-STATE.md,
and the main agent session will automatically detect and read them
Confidence
90% confidence
Finding
This example explicitly establishes a cross-context persistence channel where background or cron tasks append arbitrary content into SESSION-STATE.md for automatic ingestion by the main session. That creates a trust-boundary violation: any task able to write this file can influence later agent behavior, potentially injecting misleading instructions, exfiltration prompts, or poisoned state that the main session may treat as trusted context.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains natural-language defaults and example content exclusively in Chinese, including the default title and report categories used by the helper functions. Because the skill does not offer a language choice or explain that it is intentionally region-specific, it appears to impose a specific language/locale policy on generated session-state content.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown file uses Chinese throughout and does not indicate that the skill is region-specific or provide an opt-in choice for language. Under the policy, forcing a specific language without user choice is a natural-language policy violation.

Session Persistence

Medium
Category
Rogue Agent
Content
bash scripts/check_session_state.sh --watch

# 启动实时监控(后台守护)
nohup bash scripts/check_session_state.sh --watch --daemon > /tmp/session-state-watch.log 2>&1 &

# 停止守护
bash scripts/check_session_state.sh --stop-daemon
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The script’s stated purpose is to detect and report SESSION-STATE.md changes, but it also rewrites that file when it exceeds a size threshold. Any monitor that silently mutates the artifact it is supposed to observe creates integrity risk, can destroy historical context, and may interfere with other agents or processes relying on the full file contents.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
This block destructively overwrites SESSION-STATE.md after truncating its contents to the last 1000 lines, with no confirmation, locking, or rollback. If triggered unexpectedly or concurrently with another writer, it can permanently lose session history and corrupt state that other automation depends on.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The --stop-daemon path uses pkill -f with a broad pattern, which can terminate any matching process rather than only a daemon instance started by this skill. In shared or automated environments, that can cause denial of service or unintended interruption of unrelated tasks if process command lines overlap.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The example code writes a fixed Chinese heading (`学习更新`) into `SESSION-STATE.md`. This imposes a specific language in generated content without indicating user choice or a justified locale-specific constraint, which matches the language/locale policy concern.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The truncation notice inserted into SESSION-STATE.md is written in Chinese, while the rest of the script's interface is in English. This hard-coded locale choice appears user-facing and does not provide opt-in, fallback, or justification for a region-specific language requirement.

Static analysis

No suspicious patterns detected.