T09 · Insecure Skill Coding Practices
- Location
scripts/reply-bot.js:148- Finding
Model-Generated Reply Enables Shell Command Injection
- Content
View full analysis
&1`, { timeout: 15000 } ).toString(); return JSON.parse(result)?.data?.id; } ``` The value passed as `text` comes directly from model-generated output: ```js let replyText; try { replyText = await generateReply(effectiveTweet, context); console.log(`[reply] Generated: "${replyText}"`); } catch (e) { console.log(`[reply] Generation failed: ${e.message?.slice(0, 80)}`); continue; } if (dryRun) { console.log(`[reply] DRY RUN → tweet ${tweet.id}: "${replyText}"`); continue; } // Like the mention first — shows visibility, builds engagement likeTweet(tweet.id); // Post try { const replyId = postReply(tweet.id, replyText); ``` ### Technical Analysis `child_process.execSync()` passes the constructed command to a system shell. `JSON.stringify(text)` serializes the generated reply as a JavaScript string, but it does not safely escape the value for shell execution. In particular, command substitutions such as `$(command)` and backtick expressions remain active inside shell double quotes. Consequently, a generated reply containing shell syntax can cause local commands to execute before `xurl` receives the reply argument. The reply content originates from Gemini and is influenced by attacker-controlled tweet text and conversation context. Model output must therefore be treated as untrusted input. The combination of prompt injection, unvalidated model output, string-based shell command construction, and automatic execution creates a remote command-execution path. The tweet ID is also interpolated into the shell command without explicit numeric validation, although the primary confirmed path is through th ...[truncated 1413 chars]- Remediation
View remediation
