T09 · Insecure Skill Coding Practices
- Location
scripts/safe-install.mjs:202- Finding
Shell Command Injection Through Unvalidated Skill Name
- Content
View full analysis
Vulnerability Details
File Location:
scripts/safe-install.mjs:135-143, 202, 251, 372, 576, 767
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
js function execCommand(cmd, options = {}) { try { const result = execSync(cmd, { encoding: 'utf8', stdio: options.silent ? 'pipe' : 'inherit', ...options }); return { success: true, output: result };User-controlled
skillNameis subsequently interpolated into shell command strings:js const inspectResult = execCommand(`clawhub inspect "${skillName}"`, { silent: true }); const downloadResult = execCommand( `clawhub install "${skillName}" --dir "${tempDir}"`, { silent: true } ); const result = execCommand(`clawhub search "${skillName}"`, { silent: true }); const downloadResult = execCommand( `clawhub install "${skillName}" --dir "${tempDir}"`, { silent: true } ); const result = execCommand(`clawhub install "${skillName}"`);Technical Analysis
skillNameoriginates from command-line arguments and is inserted directly into command strings passed toexecSync. String-basedexecSyncexecution invokes a shell, so shell metacharacters and command substitutions in the Skill name are interpreted by that shell.Wrapping the input in double quotes is insufficient. An attacker can use an embedded quote to terminate the intended argument or use shell constructs that remain active inside double quotes, such as command substitution. No strict Skill-name validation is performed before the value reaches these command sinks.
The vulnerability is triggered during metadata inspection, temporary download, rating lookup, sandbox preparation, or final installation. Consequently, code execution can occur before the security checks have completed.
Attack Path
- An attacker convinces a user or automated workflow to invoke the installer with a crafted Sk ...[truncated 1040 chars]
- Remediation
View remediation
Remediation Suggestions
-
Replace string-based
execSynccalls withexecFileSyncorspawnSyncand pass arguments as an array:js import { execFileSync } from 'child_process'; const output = execFileSync( 'clawhub', ['inspect', skillName], { encoding: 'utf8', stdio: 'pipe', shell: false } ); -
Apply the same change to every
clawhub,curl, andzipinvocation. Do not construct shell commands by concatenating or interpolating variable input. -
Validate Skill identifiers using an allowlist appropriate to ClawHub naming rules. For example, only permit letters, digits, underscores, periods, hyphens, and an explicitly supported namespace separator.
-
Reject control characters, whitespace, quotes, backticks, dollar signs, and shell metacharacters before invoking any external process.
-
Add automated tests covering embedded quotes, command substitution, semicolons, newlines, and other shell-injection payload classes.
-
