T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/install-wrapper.sh:19
- Finding
- SSH option injection through an unvalidated remote host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install-wrapper.sh:19,69,85-89`; host values also originate from `scripts/install-homebrew-pack.sh:67-93,150-170` **Vulnerability Type**: SSH option injection leading to local command execution **Risk Level**: High ### Vulnerable Code ```bash # scripts/install-wrapper.sh --host) HOST="${2:-}"; shift 2 ;; ``` ```bash # scripts/install-wrapper.sh HOST=$(printf '%q' "$HOST") REMOTE_BIN=$(printf '%q' "$REMOTE_BIN") SSH_OPTS=( ``` ```bash # scripts/install-wrapper.sh run_remote() { local remote_cmd remote_cmd=$(printf '%q ' "$REMOTE_BIN" "$@") ssh "${SSH_OPTS[@]}" -T "$HOST" "bash -lc $(printf %q "$remote_cmd")" } ``` The host can also be obtained automatically from configuration without validation: ```bash # scripts/install-homebrew-pack.sh if [[ -n "$OPENCLAW_CONFIG" && -f "$OPENCLAW_CONFIG" ]] && command -v python3 >/dev/null 2>&1; then local discovered discovered="$(python3 - "$OPENCLAW_CONFIG" <<'PY' import json import sys from pathlib import Path config_path = Path(sys.argv[1]) cfg = json.loads(config_path.read_text()) channels = cfg.get("channels") or {} hosts = [] for channel in channels.values(): if not isinstance(channel, dict): continue remote_host = channel.get("remoteHost") if isinstance(remote_host, str) and remote_host.strip(): host = remote_host.strip() if host not in hosts: hosts.append(host) if len(hosts) == 1: print(hosts[0]) PY )" ``` ```bash # scripts/install-homebrew-pack.sh default_host="$(discover_default_host || true)" for tool in "${REQUESTED_TOOLS[@]}"; do host="${TOOL_HOSTS[$tool]:-$default_host}" [[ -n "$host" ]] || { echo "missing host for tool: $tool" >&2 echo "Provide --map $tool=user@host or --default-host user@host." >&2 exit 1 } remote_bin="/opt/homebrew/bin/$tool" cmd=( "$INSTALL_WRAPPER" --name "$tool" --host "$host" --remote-bin "$remote_bin" --target-di ...[truncated 2399 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Reject host values that begin with `-`, contain control characters, contain whitespace, or do not match an explicitly supported SSH destination format. 2. Parse and validate the username and hostname separately. Permit only expected username characters and valid DNS names, IPv4 addresses, or carefully handled IPv6 addresses. 3. Apply the same validation to explicit `--host` values, `--map` values, `--default-host`, and auto-discovered `remoteHost` values. 4. Do not rely on shell quoting as protection against option injection into downstream command-line tools. 5. Where supported by the SSH invocation design, use an option terminator before the destination. Validation is still required because SSH option-termination behavior and destination syntax should be tested against all supported client versions. 6. Consider invoking SSH with a fixed configuration and prohibiting caller-controlled SSH options. 7. Add negative tests covering values such as leading-hyphen destinations, embedded whitespace, control characters, malformed usernames, and injected `ProxyCommand` options. A defensive validation pattern should reject rather than normalize malformed input: ```bash validate_ssh_destination() { local destination="$1" [[ "$destination" != -* ]] || { echo "SSH destination must not begin with '-'" >&2 return 1 } [[ "$destination" =~ ^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$ ]] || { echo "Invalid SSH destination; expected user@host" >&2 return 1 } } validate_ssh_destination "$HOST" ``` The exact expression should be expanded deliberately if IPv6 or other SSH destination forms must be supported. ]]>
