Back to skill

Security audit

OpenCode ACP Control v2

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its OpenCode automation purpose, but it gives agents unsafe cleanup and generated-command workflows that can affect unrelated local sessions.

Review this skill carefully before installing. It can help automate OpenCode ACP sessions, but its cleanup logic can remove lock files for unrelated OpenClaw sessions, and its helper script outputs executable steps built from unescaped user-supplied values. Use only in an isolated environment or after revising cleanup to target the exact current session and treating generated workflow text as untrusted data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
opencode-session.sh:344
Finding
Unescaped User Input in Agent-Executable Workflow Output## Vulnerability Details **File Location**: `opencode-session.sh`, lines 344-358 **Vulnerability Type**: Command and JSON-RPC instruction injection **Risk Level**: High ### Vulnerable Code ```bash ## Step 1: Start OpenCode exec(command: "opencode acp --cwd $project", background: true, workdir: "$project") # → Save returned sessionId as PROCESS_SESSION_ID ## Step 2: Initialize process.write(PROCESS_SESSION_ID, data: '$(initialize_opencode)' + "\\n") process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_INIT) # → Expect: {"result":{"protocolVersion":1,...}} ## Step 3: Create Session process.write(PROCESS_SESSION_ID, data: '$(create_session "$project" "$mcp_servers")' + "\\n") process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_SESSION) # → Save result.sessionId as OPENCODE_SESSION_ID ## Step 4: Send Prompt process.write(PROCESS_SESSION_ID, data: '$(send_prompt "OPENCODE_SESSION_ID" "$prompt")' + "\\n") ``` The underlying JSON-RPC construction also directly interpolates these values: ```bash create_session() { local cwd="$1" local mcp_servers="$2" log INFO "Creating session in $cwd" local params="{\"cwd\":\"${cwd}\",\"mcpServers\":${mcp_servers:-[]}}" local json=$(send_jsonrpc "session/new" "$params") echo "$json" } send_prompt() { local session_id="$1" local prompt="$2" log INFO "Sending prompt (${#prompt} chars)" local params="{\"sessionId\":\"${session_id}\",\"prompt\":[{\"type\":\"text\",\"text\":\"${prompt}\"}]}" local json=$(send_jsonrpc "session/prompt" "$params") echo "$json" } ``` ### Technical Analysis Values supplied through `--project`, `--prompt`, and `--mcp` are inserted into generated command syntax and JSON-RPC messages without context-appropriate escaping or validation. The script does not directly execute the generated workflow. However, its documentation explicitly instructs CYPHER or another consuming age ...[truncated 1919 chars]
Remediation
## Remediation Suggestions 1. Stop producing executable command syntax through string interpolation. Return a structured data document whose fields remain data rather than instructions. 2. Construct all JSON-RPC messages with a real JSON serializer, such as: ```bash jq -cn \ --arg cwd "$cwd" \ --argjson mcpServers "$mcp_servers" \ '{jsonrpc:"2.0", id:1, method:"session/new", params:{cwd:$cwd, mcpServers:$mcpServers}}' ``` 3. Construct prompt messages with `jq --arg prompt "$prompt"` so quotation marks, newlines, backslashes, and control characters are escaped correctly. 4. Validate MCP input before use: ```bash jq -e 'type == "array" and all(.[]; type == "string")' \ <<< "$mcp_servers" >/dev/null ``` 5. Canonicalize the project path with `realpath`, require it to be an existing directory, and optionally restrict it to approved workspace roots. 6. Invoke processes through structured argument arrays rather than a generated shell command string. 7. Ensure the downstream agent treats script output as untrusted data. It should execute only a fixed, locally defined workflow and populate validated parameters into that workflow. 8. Add regression tests covering embedded quotation marks, newlines, backslashes, JSON delimiters, and strings resembling agent tool calls.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
opencode-session.sh:63
Finding
Global Deletion of Session Locks Across Unrelated OpenClaw Agents## Vulnerability Details **File Location**: `opencode-session.sh`, lines 63-76 and line 371; also documented repeatedly in `SKILL.md` **Vulnerability Type**: Overbroad lock-file deletion and violation of least privilege **Risk Level**: Medium ### Vulnerable Code ```bash cleanup_stale_locks() { log INFO "Cleaning stale locks (>30min old)" find ~/.openclaw/agents -name '*.lock' -mmin +30 -delete 2>/dev/null || true } cleanup_session() { if [[ -n "$PROCESS_SESSION_ID" ]]; then log INFO "Cleaning up session" # Kill process would be done by caller via process.kill # Just clean locks here rm -f ~/.openclaw/agents/*/sessions/*.lock 2>/dev/null || true fi } ``` The generated cleanup workflow contains the same unrestricted deletion: ```bash ## Step 6: Cleanup process.kill(PROCESS_SESSION_ID) exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock") ``` The behavior is also recommended in `SKILL.md`, including: ```bash exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock") ``` ### Technical Analysis The wildcard cleanup is not scoped to the OpenCode process or session created by this Skill. It targets lock files belonging to every agent beneath `~/.openclaw/agents/*/sessions/`. The `find` command applies an age check, but it still operates across all agents and does not verify whether the process associated with a lock remains alive. The `rm` command is more dangerous because it removes every matching lock regardless of age, ownership by the current workflow, or session activity. Lock files enforce coordination between processes. Removing a valid lock does not directly grant additional operating-system permissions, but it can bypass application-level mutual exclusion and allow multiple processes to act on state that should have remained exclusively locked. ### Attack Path 1. One or more unrelated OpenClaw agents have active sessions and c ...[truncated 1151 chars]
Remediation
## Remediation Suggestions 1. Record the exact lock path associated with the process and OpenCode session created by this Skill. 2. Remove only that specific lock during cleanup; do not use wildcards spanning all agents. 3. Before deleting a lock, verify: - The lock belongs to the expected session. - The recorded process identifier is no longer alive. - The lock path resolves beneath the expected session directory. - The file is a regular file and not a symbolic link. 4. If stale-lock recovery is required, use session-specific metadata and an atomic ownership check rather than age alone. 5. Replace the generated cleanup command with a fixed helper that receives a validated session identifier and computes the permitted lock path internally. 6. Refuse suspicious session identifiers containing path separators or traversal sequences. 7. Log every deleted lock with its session identifier and the evidence used to determine that it was stale. 8. Update `SKILL.md` to remove all recommendations for global wildcard lock deletion. 9. Add concurrency tests proving that cleanup of one OpenCode session cannot remove or modify locks belonging to another agent.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (22)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
| Read response | `process.poll(sessionId)` - adaptive polling |
| Health check | `process.poll(sessionId, timeout: 5000)` - only when no output >60s |
| Stop OpenCode | `process.kill(sessionId)` + cleanup locks |
| Clean locks | `exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock")` |
| List sessions | `exec(command: "opencode session list", workdir: "...")` |
| Resume session | List sessions → `session/load` |
Confidence
95% confidence
Finding
The skill explicitly directs use of a destructive shell command, rm -f ~/.openclaw/agents/*/sessions/*.lock, via exec. Allowing an agent to issue deletion commands with globbing in the user's home directory is dangerous because mistakes, environment differences, or malicious influence over execution context can cause unintended data loss or normalize unsafe command execution patterns.

Ae1

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

Tool Parameter Abuse

High
Category
Tool Misuse
Content
exec(command: "find ~/.openclaw/agents -name '*.lock' -mmin +30 -delete")

# After killing a stuck process
exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock")
```

---
Confidence
95% confidence
Finding
This recovery step again recommends direct execution of rm -f against a glob in ~/.openclaw after killing a process. Embedding destructive shell parameters in automated recovery magnifies risk because the action may happen repeatedly without review and can be triggered by false stuck detection.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```
process.kill(sessionId)
exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock")
```

---
Confidence
95% confidence
Finding
The documented final cleanup instructs an agent to kill a process and then run rm -f on a home-directory glob. Combining process control and file deletion in a routine completion path creates a strong chance of accidental destructive behavior and exceeds what many users would expect from a protocol helper.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
process.kill(sessionId)
    
    # Step 4: Clean up
    exec("rm -f ~/.openclaw/agents/*/sessions/*.lock")
    
    # Step 5: Restart
    newSessionId = startOpenCode()
Confidence
94% confidence
Finding
The recovery function hardcodes a destructive exec("rm -f ~/.openclaw/agents/*/sessions/*.lock") step, making filesystem deletion part of automated error handling. Automated destructive commands are especially risky because they may be reached during normal faults, retries, or misclassification of process state, leading to repeated unintended deletions.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
log INFO "Cleaning up session"
        # Kill process would be done by caller via process.kill
        # Just clean locks here
        rm -f ~/.openclaw/agents/*/sessions/*.lock 2>/dev/null || true
    fi
}
Confidence
95% confidence
Finding
The cleanup function deletes all matching session lock files under ~/.openclaw/agents/*/sessions using a wildcard rather than targeting locks associated with the current session. This can interfere with other active sessions, break synchronization guarantees, and potentially enable concurrent operations against resources that were protected by those locks.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Step 6: Cleanup
process.kill(PROCESS_SESSION_ID)
exec(command: "rm -f ~/.openclaw/agents/*/sessions/*.lock")

# Metrics to log:
# - duration_ms
Confidence
97% confidence
Finding
The script emits workflow instructions that direct downstream execution of a destructive shell command deleting all session lock files. Because this is packaged as part of an automation skill, it increases the chance that callers will execute the command blindly, causing cross-session disruption or corruption in environments where multiple agents or users rely on those locks.

Lp3

Medium
Category
MCP Least Privilege
Confidence
77% confidence
Finding
The skill documents substantial capabilities including ACP session control, shell execution, filesystem deletion, and MCP server usage, but the manifest does not declare any explicit tool scope or permissions. That mismatch increases the chance an agent will invoke broader tools than users expect, weakening least-privilege controls and informed consent.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
|---------|-------------|
| **Auto-retry** | Automatically retries on failure (max 3 attempts) |
| **Stuck detection** | Detects when OpenCode is not responding |
| **Lock cleanup** | Automatically removes stale lock files |
| **Adaptive polling** | Polls faster at start, slower when stable |
| **Health checks** | Periodic checks that OpenCode is alive |
| **Configurable timeouts** | Shorter timeouts with escalation |
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documented command uses rm -f on lock files under ~/.openclaw without an explicit warning that it deletes files in the user's home directory. Even with a seemingly narrow glob, destructive commands can remove files unexpectedly or normalize risky deletion behavior in agents.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The skill recommends shell commands for file deletion and maintenance actions that go beyond narrow ACP control, effectively encouraging arbitrary command execution. Expanding from protocol interaction to unrestricted shell use raises the risk of destructive actions, path mistakes, and abuse if prompts or variables are influenced by untrusted input.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
Automatic stale-lock deletion is described as routine behavior but lacks a clear warning that it mutates the filesystem. Silent deletion in recovery flows is risky because agents may perform it repeatedly and users may not realize home-directory files are being removed.

Session Persistence

Medium
Category
Rogue Agent
Content
restart from Step 2
```

### Step 4: Create Session (with retry)

```json
{"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/path/to/project","mcpServers":[]}}
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.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The cleanup sequence combines process termination with lock-file deletion but does not present safety guidance or validation steps. This can lead to abrupt state loss and unnecessary file removal if triggered during normal operation or against the wrong session.

Session Persistence

Medium
Category
Rogue Agent
Content
if attempt == 3:
    throw Error("Failed to initialize after 3 attempts")

# Create session with retry
for attempt in 1..3:
  process.write(state.processSessionId, session_new())
  response = process.poll(state.processSessionId, timeout: 10000)
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.

Session Persistence

Medium
Category
Rogue Agent
Content
process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_INIT)
# → Expect: {"result":{"protocolVersion":1,...}}

## Step 3: Create Session
process.write(PROCESS_SESSION_ID, data: '$(create_session "$project" "$mcp_servers")' + "\\n")
process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_SESSION)
# → Save result.sessionId as OPENCODE_SESSION_ID
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.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The generated workflow tells the caller to run a broad lock-file deletion command against all matching session locks without confirming ownership, scope, or whether the lock is still needed. In a shared or multi-session environment, this can disrupt unrelated agent sessions and cause denial of service or unsafe concurrent access.

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Content
# OpenCode Prompt Templates

Templates para tareas comunes de código.
Confidence
85% confidence
Finding
Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Description-Behavior Mismatch

Low
Confidence
84% confidence
Finding
The manifest says the skill controls OpenCode via ACP, but the quick reference also includes direct deletion of lock files in the user's home directory. This scope expansion is security-relevant because operators may not expect filesystem mutation from a protocol-control skill.

Description-Behavior Mismatch

Low
Confidence
80% confidence
Finding
The documentation introduces an external helper shell script that orchestrates execution, but that capability is not reflected in the manifest. Hidden operational scope makes it easier for downstream agents or users to trust the skill for ACP-only behavior while it actually drives shell-based automation.

Intent-Code Divergence

Low
Confidence
78% confidence
Finding
The documentation says the helper script can perform a dry run, but then instructs the agent to execute emitted steps in order. That contradiction can mislead operators into thinking a non-executing preview is safer than it is, increasing the chance of accidental command execution.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
This markdown file contains user-facing guidance in Spanish while the reusable prompt templates themselves are consistently written in English. Because the skill does not state that English is required or give the user a language/locale choice, it effectively steers usage toward a specific language without explicit opt-in.

Static analysis

No suspicious patterns detected.