Back to skill

Security audit

opencode-acp

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its stated coding-agent purpose, but it grants broad local file and terminal authority and can store task conversations on disk without strong permission controls.

Install only if you are comfortable letting OpenCode read, edit, and run commands in the selected project. Avoid putting secrets in task prompts, prefer oneshot mode for sensitive work, review all code changes before committing, and delete .acp_sessions entries when session continuity is no longer needed.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
opencode_acp_client.py:317
Finding

Persisted Session Conversations Are Stored in Plaintext Without Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: opencode_acp_client.py, lines 317-336 and 448-463
Vulnerability Type: Plaintext storage of potentially sensitive conversation data with permissions determined by the process umask
Risk Level: Medium

Vulnerable Code

python
def ensure_session_store_dir() -> None:
    """Create local session storage directory if missing."""
    SESSION_STORE_DIR.mkdir(parents=True, exist_ok=True)


def load_persisted_session(session_name: str) -> Optional[dict]:
    """Load persisted session metadata from disk."""
    path = session_file_path(session_name)
    if not path.exists():
        return None
    with path.open("r", encoding="utf-8") as fp:
        return json.load(fp)


def save_persisted_session(session_name: str, data: dict) -> None:
    """Persist session metadata to disk."""
    ensure_session_store_dir()
    path = session_file_path(session_name)
    with path.open("w", encoding="utf-8") as fp:
        json.dump(data, fp, ensure_ascii=False, indent=2)

The sensitive data written through this function includes complete user prompts and assistant responses:

python
history = data.get("history", [])
history.append({"role": "user", "content": args.task, "ts": int(time.time())})
assistant_text = result.get("assistantText", "")
if assistant_text:
    history.append({"role": "assistant", "content": assistant_text, "ts": int(time.time())})

data["history"] = history
data["projectDir"] = project_dir
data["updatedAt"] = int(time.time())
save_persisted_session(session_name, data)

Technical Analysis

Session mode persistently stores the complete conversation history in JSON files under the skill-local .acp_sessions/ directory. The directory is created without an explicit mode, and session files are opened with the ordinary "w" mode without enforcing owner-only permissions. Consequently, their effective permissions depend on th ...[truncated 2060 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create .acp_sessions with owner-only permissions (0700) and verify or correct permissions when the directory already exists.
  2. Create each session file with owner-only permissions (0600) rather than relying on the process umask.
  3. Write updates atomically to a securely created temporary file in the same directory, set its mode to 0600, flush and synchronize it, and then replace the destination.
  4. Refuse to follow symlinks and validate that both the storage directory and destination are owned by the expected user.
  5. Add .acp_sessions/ to .gitignore and exclude it from packaging and backup workflows unless explicitly required.
  6. Document that prompts must not contain credentials or other secrets and provide a mode that disables persistence.
  7. Implement a retention policy and a command to securely remove old session histories.
  8. If session histories must be stored on shared systems, encrypt them using credentials held outside the project directory.

Example Unix hardening:

python
def ensure_session_store_dir() -> None:
    SESSION_STORE_DIR.mkdir(mode=0o700, parents=True, exist_ok=True)
    os.chmod(SESSION_STORE_DIR, 0o700)


def save_persisted_session(session_name: str, data: dict) -> None:
    ensure_session_store_dir()
    path = session_file_path(session_name)
    flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
    if hasattr(os, "O_NOFOLLOW"):
        flags |= os.O_NOFOLLOW
    fd = os.open(path, flags, 0o600)
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as fp:
            json.dump(data, fp, ensure_ascii=False, indent=2)
    except Exception:
        os.close(fd)
        raise
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose suggests ACP collaboration for code modification, but the actual documented behavior includes local interpreter discovery, virtual environment creation, and process re-execution. This hidden expansion of behavior is dangerous because operators may approve the skill expecting coordination logic, while it also alters the local environment and executes additional bootstrapping steps.

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 · venv_utils.py (reported line 134)May include surrounding context.

python
if not venv_python.exists():
        subprocess.run(python_for_venv + ["-m", "venv", str(venv_dir)], check=True)

    env = os.environ.copy()
    env[VENV_ACTIVE_FLAG] = "1"
    exec_argv = [str(venv_python)] + (argv if argv is not None else sys.argv)
    os.execvpe(str(venv_python), exec_argv, env)

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · venv_utils.py (reported line 137)May include surrounding context.

python
env = os.environ.copy()
    env[VENV_ACTIVE_FLAG] = "1"
    exec_argv = [str(venv_python)] + (argv if argv is not None else sys.argv)
    os.execvpe(str(venv_python), exec_argv, env)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill exposes powerful capabilities including shell execution, file read/write, environment access, and MCP interaction, but does not declare a tool scope or permission boundary. In a delegation skill that explicitly launches external processes and writes to projects, this omission increases the chance of overbroad execution and unsafe use in contexts that expect least-privilege controls.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The first-time setup flow tells users to run /init-deep, which creates a hierarchical AGENTS.md knowledge base inside the target project, but it does not prominently warn that files will be written into the repository. That can lead to silent project mutation, accidental commits, or contamination of sensitive repositories during what appears to be initialization only.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill instructs the external agent to run an out-of-band openclaw system event command upon completion, which extends behavior beyond ACP task execution into host-level signaling. This creates an unnecessary command-execution path that could be repurposed for unintended actions, especially if prompt text or summaries are influenced by untrusted task content.

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 · opencode_acp_client.py (reported line 130)May include surrounding context.

python
if not opencode_bin:
            raise Exception("OpenCode binary not found")
        
        self.process = subprocess.Popen(
            [opencode_bin, "acp"],
            cwd=self.project_dir,
            stdin=subprocess.PIPE,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The client advertises terminal capability to the ACP server, allowing the remote/agent side to invoke command-execution features. In the context of a coding assistant operating on a local project, this materially expands authority from code modification into arbitrary command execution, which can lead to local file access, secret exposure, or destructive actions if the ACP peer is compromised or misbehaves.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
77% confidence
Finding

Session persistence itself is not inherently malicious, but retaining prior conversation state on disk creates a recoverable record of prompts, responses, and project metadata. In this skill context, that can include sensitive code and operational details, increasing privacy and data-retention risk compared with stateless execution.

Content

Scanner excerpt · opencode_acp_client.py (reported line 322)May include surrounding context.

python
def load_persisted_session(session_name: str) -> Optional[dict]:
    """Load persisted session metadata from disk."""
    path = session_file_path(session_name)
    if not path.exists():
        return None

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Persisted session history is written to disk without a user-facing warning, consent flow, or clear disclosure. This is dangerous because users may unknowingly place sensitive project details, credentials, or proprietary code into long-lived local storage that survives beyond the active session.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

This Python file contains natural-language docstrings, status messages, and usage text entirely in Chinese, including the title, runtime progress output, and CLI help. Because the skill does not offer a language/locale choice or justify that it is intended only for Chinese-speaking users, it violates the language/locale policy for natural-language content.

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 · opencode_monitor.py (reported line 30)May include surrounding context.

python
)
        
        try:
            result = subprocess.run(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module description is written as a Chinese-only title, and the file consistently presents its user-facing interface in Chinese. This imposes a specific language/locale on users without opt-in or any explanation that the skill is region-specific, which matches the policy violation criteria for language or locale restrictions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The printed startup and task messages are exclusively in Chinese, and the usage/help text later in the file follows the same pattern. Because the skill does not offer an alternative language or document a justified locale restriction, these strings constitute a natural-language policy issue.

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 · opencode_realtime.py (reported line 22)May include surrounding context.

python
cmd = build_client_command(project_dir=project_dir, task=task, timeout=timeout)
    
    # 使用 Popen 实时输出
    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,

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 · opencode_wrapper.py (reported line 24)May include surrounding context.

python
cmd = build_client_command(project_dir=project_dir, task=task, timeout=timeout)
    
    # 使用 Popen 实时输出
    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The usage message shown on incorrect invocation is only in Chinese, which means even basic operation guidance is restricted to one language. The file does not provide a user-selectable language option or justify a Chinese-only audience, so this violates the language/locale policy requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This file contains natural-language output and documentation exclusively in Chinese, starting with the module docstring and continuing in user-facing console messages. Because the skill does not offer a language selection or document a justified locale restriction, it violates the language/locale policy criteria.

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 · venv_utils.py (reported line 76)May include surrounding context.

python
def _python_version(command: List[str]) -> Optional[Tuple[int, int]]:
    try:
        check = subprocess.run(
            command + ["-c", "import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}')"],
            capture_output=True,
            text=True,

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 · venv_utils.py (reported line 132)May include surrounding context.

python
venv_python = _venv_python_path(venv_dir)
    if not venv_python.exists():
        subprocess.run(python_for_venv + ["-m", "venv", str(venv_dir)], check=True)

    env = os.environ.copy()
    env[VENV_ACTIVE_FLAG] = "1"

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown file states that entry scripts auto-create a local .venv in the skill directory on first run, which is a file-system modifying behavior. Although the behavior is mentioned, it is presented as operational detail rather than a user-facing warning or caution about modifying the working directory.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The skill's operational instructions, examples, rules, and user-facing guidance are all presented in English as the required interaction mode, with no option for user language preference. Under the policy rule, forcing a specific language without opt-in can be a locale/language policy issue unless explicitly justified.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The client stores conversation history and project metadata on local disk across runs, which extends data retention beyond transient ACP collaboration. That can expose sensitive prompts, code excerpts, project paths, or secrets from prior interactions to other local users, backup systems, or later unintended reuse.

Content

No source excerpt is available for this finding.