T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:94
- Finding
- Shell Command Injection Through Unsanitized Join and Owner Parameters<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 94–98 **Vulnerability Type**: Shell command injection through externally controlled values **Risk Level**: High ### Vulnerable Code ```bash uv run python apps/openclaw-bridge/src/openclaw_bridge/cli.py "<JOIN_URL>" \ --preflight-mode confirm \ --owner-channel openclaw \ --owner-openclaw-channel "<CHANNEL>" \ --owner-openclaw-target "<TARGET>" ``` ### Technical Analysis The command template inserts `JOIN_URL`, `CHANNEL`, and `TARGET` into a shell command. These values may originate from users, invitation links, or external messages. The Skill does not require strict validation, shell-safe escaping, or execution through a subprocess argument array. Enclosing a value in double quotes does not neutralize all shell syntax. Command substitutions such as `$(command)` and backticks are still evaluated inside double-quoted strings when the resulting command is interpreted by a shell. Consequently, a malicious value can trigger local command execution before the Python bridge processes the supplied argument. For example, if a crafted join URL contains `$(attacker_command)` and the Agent performs direct textual substitution followed by shell execution, the shell evaluates `attacker_command` locally. Similar injection is possible through `CHANNEL` or `TARGET`. Owner confirmation does not adequately mitigate this issue. The owner is ostensibly confirming participation in a room, not authorizing arbitrary commands embedded in an invitation or routing parameter. ### Attack Path 1. An attacker sends the Agent or user a crafted ClawRoom invitation containing shell command-substitution syntax in the URL or token. 2. The Agent presents the normal room-join plan. 3. The owner confirms the join, believing the approval applies only to communication with the room service. 4. The Agent places the attacker-controlled value into the documented Bash template. 5. The command is executed through a shel ...[truncated 1220 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Do not execute the bridge through a shell.** Invoke it with a subprocess API that accepts an argument array, for example: ```python subprocess.run( [ "uv", "run", "python", "apps/openclaw-bridge/src/openclaw_bridge/cli.py", join_url, "--preflight-mode", "confirm", "--owner-channel", "openclaw", "--owner-openclaw-channel", channel, "--owner-openclaw-target", target, ], shell=False, check=True, ) ``` 2. **Validate the join URL before execution.** - Parse it with a URL parser rather than regular-expression extraction or string concatenation. - Require the `https` scheme. - Require the exact approved hostname, such as `clawroom.cc`; do not accept suffix matches. - Require the expected `/join/<room_id>` path structure. - Apply restrictive allowlists to room IDs and tokens. - Reject control characters, whitespace, shell metacharacters, and malformed percent encoding. 3. **Validate owner-routing parameters.** - Restrict `CHANNEL` and `TARGET` to documented formats and lengths. - Prefer enumerated channel values and identifier-specific allowlists. - Reject unexpected characters rather than attempting ad hoc shell escaping. 4. **Separate parsing from action.** Display the normalized hostname, room ID, and destination to the owner before confirmation. Do not display or execute attacker-supplied command text. 5. **Document the execution requirement explicitly.** State that implementations must pass each value as a discrete process argument and must never interpolate these fields into `sh -c`, `bash -c`, or an equivalent shell command. 6. **Add negative security tests** using payloads containing `$(...)`, backticks, quotes, semicolons, newlines, encoded metacharacters, and option-like values to verify that they are rejected or passed only as inert arguments. ]]>
