Back to skill

Security audit

Execution Loop

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent agent loop-control purpose, but its stop hooks have unsafe state and file handling that can let local or repository-controlled content steer the agent or trigger command execution.

Install only with deliberate opt-in for projects where Stop-hook loop control is needed. Before enabling broadly, fix the timestamp parser, validate session IDs, hard-cap iterations, delimit or sanitize checklist text, restrict verifier tools and paths, set restrictive permissions on session state, and document cleanup and retention.

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 (4)

T01 · Skill Instruction Hijacking

Error
Location
scripts/task-completion-gate.sh:16
Finding

Repository-Controlled Checklist Content Is Injected into Agent Instructions

Content
View full analysis
/dev/null || echo "0") REMAINING_NAMES=$(jq -r '[.tasks[] | select(.done == false) | .name] | join(", ")' "$TASK_FILE" 2>/dev/null || echo "") if [ "$REMAINING" -gt 0 ]; then jq -n --arg reason "Task checklist has ${REMAINING} unchecked item(s): ${REMAINING_NAMES}. Complete these before stopping." \ '{"decision":"block","reason":$reason}' else echo '{"continue":true}' fi ``` ### Technical Analysis The hook reads `.harness-tasks.json` from the current repository and inserts unchecked task names directly into the Stop-hook blocking reason. Although `jq --arg` safely escapes the value at the JSON syntax level, it does not make the text safe for use as an instruction presented to an AI agent. A malicious repository can define a task name containing imperative text, instructions to ignore the original task, requests to access files, or directions to invoke tools. The resulting text is delivered as part of an authoritative Stop-hook response, where it may be interpreted as executable instructions rather than untrusted repository data. No length restriction, character restriction, trust-boundary marker, or separation between control instructions and task data is applied. ### Attack Path 1. An attacker commits a `.harness-tasks.json` file to a repository opened by the agent. 2. The file contains an unchecked task whose `name` embeds malicious natural-language instruc ...[truncated 1023 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ralph-stop-hook.sh:37
Finding

Timestamp Value Is Interpolated Directly into Executable Python Source

Content
View full analysis
/dev/null || \ date -u -d "$ts" +%s 2>/dev/null || \ python3 -c "import datetime; print(int(datetime.datetime.fromisoformat('$ts'.replace('Z','+00:00')).timestamp()))" 2>/dev/null || \ echo 0 } ``` The function receives timestamps from state files, including: ```bash LAST_CHECKED=$(echo "$STATE" | jq -r '.last_checked_at // ""') ``` and: ```bash EXPIRES=$(jq -r '.expires_at // ""' "$CANCEL_FILE") ``` ### Technical Analysis The `ts` value is embedded inside a Python program passed to `python3 -c`. Shell quoting does not protect the Python source-code boundary. A timestamp containing Python quote and parenthesis characters can terminate the intended string and append additional Python statements. The first two date parsers provide no security boundary. An attacker can construct a value that they reject as an invalid date while the generated Python remains syntactically valid. For example, a value shaped like the following can close the nested function calls and append another statement: ```text 2020-01-01T00:00:00')));__import__("os").system("ATTACKER_COMMAND");# ``` The resulting Python source is structurally equivalent to: ```python import datetime print(int(datetime.datetime.fromisoformat('2020-01-01T00:00:00'))) __import__("os").system("ATTACKER_COMMAND") # remaining generated source is commented out ``` Exploitation requires the attacker to influence `last_checked_at` in `ralph.json` or `expires_at` in `cancel.json`. That may be possible for another local process running as the same user, through a malicious integration sharing the state directory, or by chaining the session-ID path-traversal weakness. ### Attack Path 1. Ralp ...[truncated 1052 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ralph-init.sh:11
Finding

Unvalidated Session Identifiers Permit Path Traversal Outside the Session Directory

Content
View full analysis
[max-iterations]}" MAX_ITERATIONS="${2:-50}" SESSION_DIR="${HOME}/.openclaw/shared-context/sessions/${SESSION_ID}" mkdir -p "$SESSION_DIR/handoffs" STATE_FILE="${SESSION_DIR}/ralph.json" ``` The Stop hook also trusts a session ID supplied through hook JSON or the environment: ```bash SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""' 2>/dev/null) [ -z "$SESSION_ID" ] && SESSION_ID="${NC_SESSION:-}" [ -z "$SESSION_ID" ] && allow SESSION_DIR="${SESSIONS_DIR}/${SESSION_ID}" STATE_FILE="${SESSION_DIR}/ralph.json" ``` The cancellation script follows the same pattern: ```bash SESSION_ID="${1:?Usage: ralph-cancel.sh [reason]}" REASON="${2:-user_abort}" SESSION_DIR="${HOME}/.openclaw/shared-context/sessions/${SESSION_ID}" mkdir -p "$SESSION_DIR" ``` ### Technical Analysis Session identifiers are used as filesystem path components without validation, canonicalization, or a containment check. An identifier containing `../` components can cause the resolved path to escape `~/.openclaw/shared-context/sessions`. Several scripts then create directories, read or overwrite fixed-name JSON files, create guard files, or delete cancellation files at the escaped location. Quoting the shell variable prevents word splitting but does not prevent filesystem traversal. The issue is repeated across the script suite, so validation in only one entry point would not be sufficient. ### Attack Path 1. An attacker supplies a session ID containing traversal components through a script argument, the `NC_SESSION` ...[truncated 1208 chars]
Remediation
View remediation
&2 exit 1 ;; esac [ "${#SESSION_ID}" -le 128 ] || exit 1 ``` - Canonicalize both the sessions root and the resulting session directory, then verify that the result remains a child of the trusted root. - Do not accept path separators in session IDs. - Reject, rather than normalize, traversal sequences. - Refuse symbolic-link session directories and state files where practical. - Create directories and files with restrictive permissions, such as `umask 077`. - Add tests for `../`, absolute paths, repeated separators, encoded separators, control characters, and symbolic-link escape cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ralph-stop-hook.sh:7
Finding

Persistent Loop Omits the Documented Context-Exhaustion Safety Valve

Content
View full analysis
2 hours idle) # 4. max iterations reached # # NOTE: Context usage >= 95% safety valve is NOT implemented because # Claude Code transcripts do not contain context_window_size. That data # is only available via the statusLine stdin pipe (HUD plugins), not # accessible to hook scripts. Claude Code's own reactive compaction # handles context overflow independently. ``` The primary Skill documentation describes a different guarantee: ```text The core guarantee is five safety valves (context >=95%, 401/403, cancel TTL, idle 2h, iteration limit), with safety valves taking priority over all blocking logic. ``` The implementation nevertheless continues to block stopping whenever Ralph remains active and its other conditions have not fired: ```bash # Block stop and increment iteration NEW_ITERATION=$(( ITERATION + 1 )) NOW=$(date -u +%FT%TZ) UPDATED=$(echo "$STATE" | jq \ --argjson iter "$NEW_ITERATION" \ --arg ts "$NOW" \ '.iteration = $iter | .last_checked_at = $ts') write_atomic "$STATE_FILE" "$UPDATED" block_with_reason "[RALPH LOOP ${NEW_ITERATION}/${MAX}] Task is NOT done. Do NOT rationalize that the remaining work can be done in a follow-up. Do NOT claim completion with caveats like 'mostly done' or 'should work'. Check your original task and verify EVERY requirement is met before attempting to stop again." ``` ### Technical Analysis The documentation identifies context utilization of at least 95% as the highest-priority condition that must always allow termination ...[truncated 1403 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (32)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill claims execution-loop control but also performs content-based gating on assistant language such as hedging detection. Mixing output censorship with stop-control can block legitimate completions or coerce the agent into producing overstated certainty, which is especially risky in security- or code-changing workflows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill claims execution-loop control but also performs content-based gating on assistant language such as hedging detection. Mixing output censorship with stop-control can block legitimate completions or coerce the agent into producing overstated certainty, which is especially risky in security- or code-changing workflows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill claims execution-loop control but also performs content-based gating on assistant language such as hedging detection. Mixing output censorship with stop-control can block legitimate completions or coerce the agent into producing overstated certainty, which is especially risky in security- or code-changing workflows.

Content

No source excerpt is available for this finding.

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · references/headless-config.md (reported line 19)May include surrounding context.

bash
# Headless 执行模板
claude -p \
  --max-turns 50 \
  --allowedTools "Read,Write,Edit,Bash,Glob,Grep" \
  "$(cat <<'PROMPT'

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · references/headless-config.md (reported line 37)May include surrounding context.

bash
# Pipeline 中的第 2 阶段
claude -p --max-turns 30 \
  "$(cat <<PROMPT
[阶段 2/4] 实现阶段。前序阶段(Research)已完成,结果见 .working-state/research-output.md。
本阶段预算:30 轮。完成后将结果写入 .working-state/implementation-output.md。

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The agent-type Stop hook delegates completion checking to a subagent with broad Read, Grep, and Bash access, which exceeds the minimum privilege needed for stop-loop control. Because this runs automatically at termination time, it can trigger autonomous file inspection and command execution in a sensitive repository context, increasing the risk of unintended command execution, data exposure, or abuse through adversarial task/repo content.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_doubt_gate.py (reported line 15)May include surrounding context.

python
@pytest.fixture
def env(tmp_path):
    e = os.environ.copy()
    e["HOME"] = str(tmp_path)
    sessions = tmp_path / ".openclaw" / "shared-context" / "sessions"
    sessions.mkdir(parents=True)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_drift_reanchor.py (reported line 15)May include surrounding context.

python
@pytest.fixture
def env(tmp_path):
    e = os.environ.copy()
    e["HOME"] = str(tmp_path)
    sessions = tmp_path / ".openclaw" / "shared-context" / "sessions"
    sessions.mkdir(parents=True)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_ralph.py (reported line 16)May include surrounding context.

python
@pytest.fixture
def env(tmp_path):
    e = os.environ.copy()
    e["HOME"] = str(tmp_path)
    sessions = tmp_path / ".openclaw" / "shared-context" / "sessions"
    sessions.mkdir(parents=True)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · tests/test_task_completion.py (reported line 16)May include surrounding context.

python
@pytest.fixture
def env(tmp_path):
    e = os.environ.copy()
    e["HOME"] = str(tmp_path)
    sessions = tmp_path / ".openclaw" / "shared-context" / "sessions"
    sessions.mkdir(parents=True)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill describes shell scripts, file reads/writes, environment use, and stop hooks, but it does not declare any explicit tool scope or allowed-tools policy. That creates an under-specified execution boundary: a consumer may enable the skill without realizing it needs shell and filesystem capabilities, increasing the risk of unintended command execution or state manipulation.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes broad phrases like 'task completion', 'headless mode', and 'agent drifts', which are likely to match ordinary conversations and activate the skill unexpectedly. In this context, accidental activation is more dangerous because the skill can block stopping, inject control messages, and influence shell-backed hooks and filesystem state.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This markdown file presents the operational instructions entirely in Chinese, including section headings and usage guidance. Under the policy, forcing a specific language without user opt-in or documented justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The entire skill document is written in Chinese while the embedded enforcement logic also scans for both English and Chinese hedging phrases, but the document does not state that the skill is region-specific or offer a language choice. This creates a natural-language policy concern because it effectively constrains usage and comprehension to a specific locale without documented opt-in or justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill explicitly stores the original prompt in sessions/<session-id>/reanchor.json, which can persist sensitive user input, secrets, internal paths, or proprietary task details to disk. Because this skill is designed for long-running/headless execution, the stored prompt may remain longer than intended and be accessible to other local users, backup systems, logs, or later processes if filesystem permissions and retention are not tightly controlled.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Most explanatory content in this markdown file is written in Chinese, while the surrounding headings and structure are in English. This creates a natural-language locale constraint for users without any opt-in, alternative language, or justification that the skill is region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This markdown file presents the operational guidance and example messages entirely in Chinese, which effectively forces a specific language for users or downstream agents. The policy allows locale constraints only when user opt-in is offered or the regional limitation is clearly justified, neither of which is present here.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
77% confidence
Finding

The manifest describes this skill as execution-loop control for agents that stop early, drift, or need headless execution control. This file states that the same mechanism underpins a larger 'ralph/autopilot/team' persistent execution system with nine prioritized modes, which broadens the documented behavior from focused loop control to a wider orchestration framework.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The manifest says the skill is used '在 headless 模式下需要执行控制时使用', implying applicability to headless execution control. However, this file explicitly says the Ralph stop-hook mechanism is '仅适用于 interactive 模式' and that headless mode has no Stop event loop, so the documented implementation here does not actually serve the headless use case it is associated with.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation describes an automatic hook that can block termination and perform file reads, grep, and bash-based testing without explicit warning, consent, or operational safeguards. In this skill context, autonomy at stop time is especially risky because users may not expect an end-of-task check to invoke tools or execute commands, which can lead to surprise side effects and unsafe operation in headless or semi-attended workflows.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This markdown file’s operative instructions and explanations are primarily in Chinese, which effectively forces a specific language for users consuming the skill. The policy allows locale constraints only when the skill offers opt-in or clearly documents a justified regional/compliance reason, neither of which appears here.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script persists session-derived identifiers and per-session state under a user home directory without any disclosure, consent, retention control, or access restrictions beyond default filesystem behavior. Even though the stored fields shown here are limited, persistent session metadata can enable tracking, cross-session correlation, and unintended exposure if the local environment is shared, backed up, or later expanded to include more sensitive task content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

The code extracts last_assistant_message from the incoming payload and uses session state to support context anchoring, indicating the hook processes conversational content. Because this automatic processing occurs in a background stop hook with no visible warning, users are not informed that conversation-related data may be examined and retained.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/test_doubt_gate.py (reported line 25)May include surrounding context.

python
def run_doubt_gate(env, last_msg="", session_id="test-sess"):
    env["NC_SESSION"] = session_id
    inp = json.dumps({"last_assistant_message": last_msg})
    result = subprocess.run(
        ["bash", str(SCRIPTS_DIR / "doubt-gate.sh")],
        capture_output=True, text=True, env=env, input=inp, timeout=10,
    )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/test_doubt_gate.py (reported line 74)May include surrounding context.

python
e.pop("NC_SESSION", None)
        e["NC_SESSION"] = ""
        inp = json.dumps({"last_assistant_message": "I think maybe this works"})
        result = subprocess.run(
            ["bash", str(SCRIPTS_DIR / "doubt-gate.sh")],
            capture_output=True, text=True, env=e, input=inp, timeout=10,
        )