T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/repo-detail.mjs:13
- Finding
- Shell Command Injection Through Repository Name<![CDATA[ ## Vulnerability Details **File Location**: `scripts/repo-detail.mjs`, lines 13–62 **Vulnerability Type**: OS command injection through unsafe shell command construction **Risk Level**: High ### Vulnerable Code ```javascript function parseArgs() { const args = process.argv.slice(2); if (args.length < 1) { return null; } return args[0]; // repo full name, e.g., "microsoft/autogen" } // Call GitHub API async function fetchRepoDetails(repoFullName) { const url = `${GITHUB_API}/${repoFullName}`; const headers = [ '-H "Accept: application/vnd.github.v3+json"', '-H "User-Agent: GitHub-Research-Skill"' ]; if (process.env.GITHUB_TOKEN) { headers.push(`-H "Authorization: token ${process.env.GITHUB_TOKEN}"`); } const cmd = `curl -s ${headers.join(' ')} "${url}"`; try { const result = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }); return JSON.parse(result); } catch (error) { console.error('Error fetching repo details:', error.message); return null; } } // Fetch contributor statistics async function fetchContributors(repoFullName) { const url = `${GITHUB_API}/${repoFullName}/contributors?per_page=10`; const headers = [ '-H "Accept: application/vnd.github.v3+json"', '-H "User-Agent: GitHub-Research-Skill"' ]; if (process.env.GITHUB_TOKEN) { headers.push(`-H "Authorization: token ${process.env.GITHUB_TOKEN}"`); } const cmd = `curl -s ${headers.join(' ')} "${url}"`; try { const result = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }); return JSON.parse(result); } catch (error) { return []; } } ``` The later validation is insufficient: ```javascript const [owner, repo] = repoFullName.split('/'); if (!owner || !repo) { console.error('❌ 格式错误,请使用 "owner/repo" 格式'); process.exit(1); } ``` ### Technical Analysis The repository name originates directly from a command-line argument and is interpolated into a shell command passed ...[truncated 2350 chars]
- Remediation
- <\/[A-Za-z0-9._-]{1,100}$/; if (!REPOSITORY_PATTERN.test(repoFullName)) { throw new Error('Invalid repository identifier'); } ``` 4. If invoking `curl` is unavoidable, use `execFileSync()` or `spawnSync()` with a fixed executable and a separate argument array, with `shell: false`. Do not concatenate arguments into a command string. 5. Avoid exposing authorization values in command strings, process listings, or error output. Keep tokens in HTTP header objects and ensure thrown errors do not include them. 6. Add regression tests using repository arguments containing quotes, semicolons, command substitutions, newlines, redirections, extra slashes, and option-like prefixes. Tests should verify rejection and confirm that no child shell is invoked. ]]>
