T09 · Insecure Skill Coding Practices
Error
- Location
- dashboard.js:129
- Finding
- Shell Command Injection Through Unvalidated Skill Slugs## Vulnerability Details **File Location**: `dashboard.js:129-132`, `dashboard.js:269-271`, `dashboard.js:294-296`, `dev-dashboard.js:100-102`, `commands/update.js:15-17`, `commands/update.js:33-35`, and `commands/uninstall.js:18-20` **Vulnerability Type**: OS command injection through `child_process.exec` **Risk Level**: High ### Vulnerable Code `dashboard.js:129-132`: ```js async function checkUpdate(skill) { try { const output = await execCommand(`clawhub inspect ${skill.slug} --json`); const remote = JSON.parse(output); ``` `dashboard.js:269-271`: ```js async function executeUpdate(skillSlug) { try { const output = await execCommand(`clawhub update ${skillSlug}`); ``` `dashboard.js:294-296`: ```js async function executeUninstall(skillSlug) { try { await execCommand(`clawhub uninstall ${skillSlug}`); ``` `dev-dashboard.js:100-102`: ```js async function fetchClawhubData(slug) { try { const output = await execCommand(`clawhub inspect ${slug} --json`); ``` `commands/update.js:15-17`: ```js async function checkUpdate(skillSlug) { return new Promise((resolve, reject) => { exec(`clawhub inspect ${skillSlug} --json`, { encoding: 'utf8', timeout: 30000 }, (error, stdout, stderr) => { ``` `commands/update.js:33-35`: ```js async function executeUpdate(skillSlug) { return new Promise((resolve, reject) => { exec(`clawhub update ${skillSlug}`, { encoding: 'utf8', timeout: 60000 }, (error, stdout, stderr) => { ``` `commands/uninstall.js:18-20`: ```js async function executeUninstall(skillSlug, stateFile) { return new Promise((resolve, reject) => { exec(`clawhub uninstall ${skillSlug}`, { encoding: 'utf8', timeout: 60000 }, (error, stdout, stderr) => { ``` ### Technical Analysis The affected functions concatenate a skill slug into a command string passed to Node.js `child_process.exec`. This API invokes a command she ...[truncated 2004 chars]
- Remediation
- ## Remediation Suggestions - Replace `child_process.exec` with `execFile` or `spawn` and pass each argument separately with shell processing disabled: ```js const { execFile } = require('child_process'); execFile( 'clawhub', ['inspect', skillSlug, '--json'], { encoding: 'utf8', timeout: 30000 }, callback ); ``` - Apply validation inside every exported function immediately before execution. For example: ```js function validateSkillSlug(value) { if (typeof value !== 'string' || !/^[A-Za-z0-9_-]+$/.test(value)) { throw new Error('Invalid skill slug'); } return value; } ``` - Do not rely exclusively on validation performed while parsing `clawhub list`, because direct callers can bypass that path. - Reject slugs containing whitespace, shell metacharacters, path separators, control characters, or Unicode look-alike characters. - Use a centralized command wrapper that accepts an executable and argument array rather than a complete command string. - Add tests covering separators, substitutions, redirects, newlines, and other shell metacharacters.
