T09 · Insecure Skill Coding Practices
Error
- Location
- install.cjs:111
- Finding
- Shell Command Injection Through User-Controlled Search and Skill Names## Vulnerability Details **File Location**: `install.cjs`, lines 111, 184, and 225 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```javascript const output = execSync(`npx clawhub search ${query} 2>&1`, { encoding: 'utf-8', cwd: SKILLS_DIR }); ``` ```javascript execSync(`unzip -q "${zipPath}" -d "${extractDir}"`, { stdio: 'inherit' }); ``` ```javascript execSync(`npx clawhub install ${skillName} --force`, { encoding: 'utf-8', cwd: SKILLS_DIR, stdio: 'inherit' }); ``` ### Technical Analysis `query` and `skillName` are derived directly from command-line arguments. They are interpolated into command strings passed to `execSync`, which invokes a system shell. Shell metacharacters, command substitutions, redirections, or embedded quotation marks contained in these arguments are interpreted by the shell rather than treated solely as literal data. Quoting `zipPath` and `extractDir` does not provide sufficient protection because a malicious skill name can contain a quotation mark that terminates the quoted argument. The vulnerable search and installation fallback commands become reachable when the corresponding API operation fails. The `unzip` invocation is reached after a skill archive has been downloaded. ### Attack Path 1. An attacker causes the tool to be invoked with a crafted search query or skill name containing shell syntax. 2. For the search or installation fallback, the attacker causes or waits for the ClawHub API operation to fail. 3. The crafted value is inserted into an `execSync` command string. 4. The operating-system shell parses the injected syntax. 5. The attacker's command executes with the privileges of the user running the installer. For the extraction command, a crafted skill name can influence `zipPath` and `extractDir`; embedded shell quoting or substitution syntax can escape the intended arguments when `unzip` is invok ...[truncated 462 chars]
- Remediation
- ## Remediation Suggestions - Replace shell-based `execSync` calls with `execFileSync` or `spawnSync`, passing arguments as an array: ```javascript execFileSync('npx', ['clawhub', 'search', query], { encoding: 'utf8', cwd: SKILLS_DIR, stdio: 'inherit', shell: false }); execFileSync('unzip', ['-q', zipPath, '-d', extractDir], { stdio: 'inherit', shell: false }); execFileSync('npx', ['clawhub', 'install', skillName, '--force'], { cwd: SKILLS_DIR, stdio: 'inherit', shell: false }); ``` - Validate skill names with a restrictive allowlist, such as `^[A-Za-z0-9._-]+$`. - Reject path separators, control characters, shell metacharacters, and names beginning with an option prefix. - Avoid invoking `npx` in a way that can download packages dynamically. Resolve and execute a trusted, pinned CLI binary instead. - Add tests covering semicolons, command substitutions, quotation marks, newlines, redirections, and option-injection payloads.
