Back to skill

Security audit

My Shell

Security checks for vulnerabilities and agentic risk

Overview

This skill is a raw shell runner with weak command controls and a malformed metadata file that contains an unexpected shell overwrite command.

Install only if you intentionally want broad local shell execution from this skill and can run it in a tightly limited environment. The package should be fixed before general use: make metadata valid JSON, remove the heredoc overwrite command, replace shell-string execSync calls with argument-array execution, and add a real approval or sandbox boundary for dangerous commands.

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

T09 · Insecure Skill Coding Practices

Error
Location
handler.js:11
Finding
Host Shell Command Injection Through Unsafe tmux Invocation<![CDATA[ ## Vulnerability Details **File Location**: `handler.js`, lines 11-14 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js function sendCommand(cmd) { const escaped = cmd.replace(/"/g, '\\"'); execSync(`tmux send-keys -t claw "${escaped}" C-m`); } ``` ### Technical Analysis The user-controlled `cmd` value is interpolated into a command string passed to `execSync`. Node.js consequently invokes a host shell to interpret the resulting string. Escaping only double quotes is insufficient because content inside double quotes still supports shell command substitution through `$(...)` and backticks. Shell metacharacters can therefore cause commands to execute in the host shell before `tmux send-keys` runs. This violates the documented security boundary that commands are executed only inside the tmux session named `claw`. The command substitution also occurs after the dangerous-command filter has inspected the original text, so payloads can combine this injection flaw with filter evasion. ### Attack Path 1. An attacker or untrusted agent supplies a tool input such as: ```json { "command": "$(id > /tmp/claw-host-execution)" } ``` 2. `isDangerous` does not identify this payload as prohibited. 3. `sendCommand` places the input inside a double-quoted shell command. 4. The host shell evaluates `$(id > /tmp/claw-host-execution)` before invoking tmux. 5. The injected command runs directly under the account hosting the skill, outside the intended tmux execution context. 6. The attacker can replace `id` with other commands available to that account to access files, invoke local programs, or modify user-owned resources. ### Impact Assessment Successful exploitation provides arbitrary command execution with the operating-system privileges of the Node.js skill process. The attacker can read or modify files accessible to that account, access environment-dependent secrets, execute installed tools, and al ...[truncated 249 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct a shell command by interpolating user input. Invoke tmux directly with an argument array so that the command is passed as data rather than interpreted by a host shell: ```js const { execFileSync } = require("node:child_process"); function sendCommand(cmd) { execFileSync("tmux", ["send-keys", "-t", "claw", "--", cmd, "C-m"]); } ``` Apply the same no-shell argument-array pattern to all tmux operations: ```js execFileSync("tmux", ["has-session", "-t", "claw"], { stdio: "ignore" }); execFileSync("tmux", ["new-session", "-s", "claw", "-d"]); const buf = execFileSync( "tmux", ["capture-pane", "-t", "claw", "-p", "-S", "-200"] ); ``` Additional hardening should include: - Run the skill under a dedicated, minimally privileged operating-system account. - Use a controlled environment and restricted `PATH`. - Apply process sandboxing and filesystem restrictions where available. - Add regression tests containing `$()`, backticks, quotes, newlines, semicolons, and other shell metacharacters. - Avoid relying on character escaping as an alternative to eliminating shell interpretation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
handler.js:24
Finding
Dangerous-Command Denylist Can Be Bypassed<![CDATA[ ## Vulnerability Details **File Location**: `handler.js`, lines 24-45 **Vulnerability Type**: Inadequate command validation and safety-control bypass **Risk Level**: Medium ### Vulnerable Code ```js function isDangerous(cmd) { const bad = ["sudo", " rm ", " rm-", "reboot", "shutdown", "mkfs", "dd "]; const lower = ` ${cmd.toLowerCase()} `; return bad.some(k => lower.includes(k)); } // MAIN ENTRYPOINT // OpenClaw will call this function when using the skill tool async function claw_shell_run(input) { const { command } = input; if (!command || typeof command !== "string") { return { error: "command is required" }; } if (isDangerous(command)) { return { error: "dangerous_command", message: `Command looks dangerous. Ask the user for explicit approval before running: ${command}` }; } ``` ### Technical Analysis The safety control uses substring matching against a short denylist. This does not parse shell syntax and cannot reliably determine which executable will ultimately run. The control can be bypassed through absolute or relative executable paths, shell expansions, aliases, interpreters, alternate utilities, spacing variations, or commands absent from the list. For example, the filter looks for the exact substring `" rm "`, but an absolute executable path such as `/bin/rm` does not contain that substring. The implementation also has no trusted approval token or persisted authorization state. Although the returned message tells the caller to request explicit approval, an approved command would still be rejected if submitted unchanged. Conversely, a syntactically modified command can evade the filter without approval. ### Attack Path 1. The attacker supplies a destructive command using an absolute path, for example: ```json { "command": "/bin/rm -rf /tmp/target-directory" } ``` 2. The padded lowercase input is `" /bin/rm -rf /tmp/target-directory "`. 3. It does not contain the denylist en ...[truncated 970 chars]
Remediation
<![CDATA[ ## Remediation Suggestions A substring denylist must not be treated as a security boundary for arbitrary shell commands. Prefer one of the following designs: 1. Replace arbitrary shell access with a strict allowlist of supported executables and validated argument schemas. 2. If arbitrary commands are a required feature, enforce explicit authorization in a trusted component outside the command text. 3. Return a cryptographically random, short-lived approval identifier when a command is blocked. 4. Store the exact command, requester identity, expiration time, and approval state server-side. 5. Execute only the stored command after a trusted user approves that identifier; do not accept a rewritten command as proof of approval. 6. Run approved commands with least privilege and enforce filesystem, process, network, and resource restrictions. If command classification remains as defense in depth, parse commands using a shell-aware parser and resolve executable paths before applying policy. This still must not replace isolation or trusted approval enforcement. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
_meta.json:27
Finding
Metadata File Contains Executable Shell Commands and Is Not Valid JSON<![CDATA[ ## Vulnerability Details **File Location**: `_meta.json`, lines 27-40 **Vulnerability Type**: Unsafe configuration packaging and metadata integrity issue **Risk Level**: Low ### Vulnerable Code ```sh cat > ~/.openclaw/skills/claw-shell/_meta.json << 'EOF' { "owner": "gdwebw", "slug": "my-shell", "displayName": "my-shell", "latest": { "version": "1.0.0", "publishedAt": 1769948954608 }, "history": [] } EOF ``` ### Technical Analysis The file is named `_meta.json`, but its contents include comments and an unquoted shell command with a heredoc. It is therefore not valid JSON. The shell fragment writes new metadata to `~/.openclaw/skills/claw-shell/_meta.json`, replacing the file with owner and slug values that differ from the earlier commented metadata. A strict metadata parser will reject the current file. If a user, installer, or automation system mistakenly treats it as a shell script, it will overwrite local skill metadata. No automatic execution path for this fragment was identified in the audited files. Consequently, this is a packaging and unsafe-configuration issue rather than confirmed persistence or confirmed malicious-code execution. ### Attack Path 1. A user or installation process assumes `_meta.json` contains setup instructions or otherwise passes it to a shell. 2. The shell executes the `cat` heredoc. 3. The installed metadata file under the user's home directory is overwritten. 4. Subsequent tooling reads the replacement owner, slug, and display name, potentially causing attribution errors or inconsistent package state. Under normal strict JSON parsing, the more immediate outcome is a parsing failure rather than command execution. ### Impact Assessment Likely impacts include metadata parser failure, installation or update errors, inconsistent package identity, and incorrect attribution. If the file is accidentally executed, it modifies the current user's OpenClaw skill metadata. The observed command does not ...[truncated 170 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace `_meta.json` with one valid JSON document containing the intended canonical metadata. Remove all comments, shell syntax, heredocs, and superseded owner records. For example: ```json { "owner": "gdwebw", "slug": "my-shell", "displayName": "my-shell", "latest": { "version": "1.0.0", "publishedAt": 1769948954608 }, "history": [] } ``` The publisher should also: - Verify which owner and slug values are authoritative. - Place installation or migration commands in separately named, reviewed scripts. - Never instruct users or automation to execute files presented as JSON. - Add CI validation that parses every JSON file with a strict JSON parser. - Add package-integrity checks to detect unexpected metadata changes before publication. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Intent-Code Divergence

High
Confidence
99% confidence
Finding
This is a true vulnerability: a file presented as static metadata contains shell redirection commands that write to ~/.openclaw/skills/claw-shell/_meta.json and replace the manifest with different owner/slug values. Embedding executable shell content in a metadata file is deceptive and dangerous because any tooling or user that copies, templates, or executes the content could silently overwrite another skill's manifest and tamper with installed skill identity.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill executes arbitrary user-supplied shell commands by injecting them into a persistent tmux session with no built-in confirmation, allowlist, or meaningful sandboxing. The lightweight substring-based 'dangerous' filter is easily bypassed and misses many destructive or exfiltration-capable commands, so an attacker or unsafe prompt flow could run arbitrary code on the host.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
handler.js:5