T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/eliza-adapter.mjs:27
- Finding
- OS Command Injection Through Agent-Controlled Token Input## Vulnerability Details **File Location**: `scripts/eliza-adapter.mjs`, lines 27–33 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js const { execSync } = await import('child_process'); const skillDir = new URL('.', import.meta.url).pathname; const result = execSync( `node ${skillDir}scripts/buzz-scan.mjs --token "${message.content.text}" --json`, { encoding: 'utf-8', timeout: 30000 } ); return { text: result }; ``` ### Technical Analysis The `BUZZ_TOKEN_INTELLIGENCE` action interpolates `message.content.text` directly into a command string passed to `child_process.execSync()`. `execSync()` executes the string through a system shell. Wrapping attacker-controlled input in double quotes does not make it safe: shell command substitutions such as `$(command)` and backtick substitutions remain active inside double-quoted strings. Shell metacharacters that interact with the surrounding command may likewise become exploitable depending on the supplied text and shell. Because action messages may originate from untrusted or indirectly attacker-controlled agent input, the token parameter crosses a trust boundary before reaching a command shell. There is no address-format validation, length restriction, escaping, or argument separation. The computed `skillDir` is also inserted without shell-safe argument separation. In addition, because `import.meta.url` already points to a file under `scripts/`, appending `scripts/buzz-scan.mjs` appears likely to produce a duplicated `scripts/scripts/` path. That path issue may impair functionality but does not mitigate the injection because shell substitutions occur while the shell evaluates the command. ### Attack Path 1. An attacker provides token-analysis text containing a shell substitution, such as a token-like value with `$(attacker_command)`. 2. The agent passes that text to `BUZZ_TOKEN_INTELLIGENCE.handler` as `message.content.text`. 3. The handler embeds the text in the co ...[truncated 1254 chars]
- Remediation
- ## Remediation Suggestions Eliminate shell interpretation by using `execFileSync()` or `spawnSync()` with an argument array: ```js const { execFileSync } = await import('child_process'); const scriptPath = new URL('./buzz-scan.mjs', import.meta.url); const token = message.content.text; const result = execFileSync( process.execPath, [scriptPath.pathname, '--token', token, '--json'], { encoding: 'utf-8', timeout: 30000, shell: false } ); return { text: result }; ``` Apply defense in depth: 1. Validate that the input is a string and impose a strict length limit. 2. Require a supported chain to be selected explicitly. 3. Validate the token against the selected chain's address syntax: - Ethereum and BSC: a properly formed hexadecimal contract address. - Solana: a valid base58 public key of the expected decoded length. 4. Reject token names in this execution path unless they are resolved through a non-shell API flow. 5. Avoid manual shell escaping as the primary defense; pass every argument separately. 6. Run the adapter with a restricted operating-system account, minimal filesystem access, a constrained environment, and limited outbound network permissions. 7. Add regression tests containing command-substitution and shell-metacharacter payloads to confirm that they are treated only as literal arguments. 8. Correct the scanner path by resolving `./buzz-scan.mjs` relative to `import.meta.url`, rather than appending another `scripts/` directory.
