T09 · Insecure Skill Coding Practices
- Location
scripts/get-hot-trend.js:13- Finding
OS Command Injection Through an Unvalidated Command-Line Argument
- Content
View full analysis
Vulnerability Details
File Location:
scripts/get-hot-trend.js, lines 13-18 and line 73
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
javascript function getHotTrend(limit = 10) { try { const scriptPath = path.join(__dirname, 'douyin.js'); const output = execSync(`node "${scriptPath}" hot ${limit}`, { encoding: 'utf-8', cwd: path.dirname(scriptPath) });The attacker-controlled value originates here:
javascript const limit = process.argv[2] || 10;Technical Analysis
The
limitvalue is read directly fromprocess.argv[2]without type validation or range enforcement. It is then interpolated into a string passed tochild_process.execSync.execSyncexecutes its string through a system shell. Consequently, shell metacharacters contained inlimit, such as semicolons, command substitutions, redirections, or pipes, are interpreted as shell syntax rather than as a numeric argument todouyin.js.Although the intended value is a count, the application does not ensure that it is an integer. Quoting
scriptPathdoes not protect the separately interpolatedlimitvalue.Attack Path
-
An attacker obtains the ability to invoke
scripts/get-hot-trend.jsor influence the argument supplied by an automation or cron integration. -
The attacker supplies a crafted value instead of a numeric limit. For example:
bash node scripts/get-hot-trend.js '10; touch /tmp/clawhub-command-injection #' -
The value is assigned to
limitwithout validation. -
The generated shell command becomes equivalent to:
bash node "/project/scripts/douyin.js" hot 10; touch /tmp/clawhub-command-injection # -
The shell executes both the intended Node.js script and the injected command.
More damaging payloads could read accessible files, modify project data, invoke network utilities, or ex ...[truncated 849 chars]
-
- Remediation
View remediation
Remediation Suggestions
Avoid invoking a shell. Use
execFileSyncorspawnSyncwith a discrete argument array:javascript const { execFileSync } = require('child_process'); function getHotTrend(limit = 10) { const parsedLimit = Number(limit); if (!Number.isInteger(parsedLimit) || parsedLimit < 1 || parsedLimit > 50) { throw new TypeError('The limit must be an integer between 1 and 50.'); } const scriptPath = path.join(__dirname, 'douyin.js'); return execFileSync( process.execPath, [scriptPath, 'hot', String(parsedLimit)], { encoding: 'utf-8', cwd: path.dirname(scriptPath), timeout: 15000 } ); }Additional hardening measures:
- Validate the argument before it reaches any process-execution function.
- Enforce a conservative numeric range to prevent resource abuse.
- Use
process.execPathrather than relying on shell resolution ofnode. - Run the skill under a dedicated, least-privileged account.
- Add automated tests containing shell metacharacters and verify that they are rejected.
