T09 · Insecure Skill Coding Practices
Error
- Location
- index.js:57
- Finding
- OS Command Injection Through the Output Path<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 57–61 **Vulnerability Type**: OS command injection through unsanitized shell interpolation **Risk Level**: High ### Vulnerable Code ```js const outMp3 = outputPath.replace(/\.wav$/i, '.mp3'); execSync( `ffmpeg -y -i "${tmpWav}" -codec:a libmp3lame -b:a 128k -ar 44100 -ac 1 "${outMp3}"`, { stdio: 'ignore', timeout: 30000 } ); ``` ### Technical Analysis The caller-controlled `outputPath` value is assigned to `outMp3` and interpolated directly into a command passed to `child_process.execSync()`. Because `execSync()` invokes a shell, shell metacharacters contained in the path are interpreted as command syntax. Surrounding the interpolated path with double quotes does not prevent exploitation. An attacker can include a double quote to terminate the quoted argument, append a command using shell separators such as `;`, and comment out the remainder. The `.replace()` operation only changes a trailing `.wav` extension and does not validate or escape shell syntax. This flaw is reachable whenever an untrusted or insufficiently trusted caller can control the `outputPath` argument passed to the exported `speak()` function. ### Attack Path 1. An attacker gains control over the `outputPath` supplied by an application, automation workflow, messaging integration, or other caller of `speak()`. 2. The attacker supplies a value containing shell syntax, for example: ```text audio.mp3"; touch /tmp/pwn; # ``` 3. The value is interpolated into the shell command, producing behavior equivalent to: ```sh ffmpeg ... "audio.mp3"; touch /tmp/pwn; #" ``` 4. After the `ffmpeg` command is processed, the shell executes the injected `touch /tmp/pwn` command. 5. An attacker could replace this demonstration command with any command available to the operating-system account running the Node.js process. Successful exploitation requires the TTS API request to complete and execution to reach the ` ...[truncated 806 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid invoking a shell. Replace `execSync()` with `execFileSync()` or `spawnSync()` and pass every argument separately: ```js const { execFileSync } = require('child_process'); const outMp3 = outputPath.replace(/\.wav$/i, '.mp3'); execFileSync('ffmpeg', [ '-y', '-i', tmpWav, '-codec:a', 'libmp3lame', '-b:a', '128k', '-ar', '44100', '-ac', '1', outMp3, ], { stdio: 'ignore', timeout: 30000, }); ``` Passing arguments as an array prevents the output path from being interpreted as shell syntax. Apply additional defense-in-depth controls: 1. Require `outputPath` to be a non-empty string with an approved extension. 2. Resolve the path with `path.resolve()` and restrict it to a designated output directory. 3. Reject paths containing null bytes or paths that escape the designated directory. 4. Run the process under a dedicated, least-privileged operating-system account. 5. Place temporary-file cleanup in a `finally` block so failed conversions do not leave files behind. 6. Add security tests using paths containing quotes, semicolons, command substitutions, spaces, and traversal sequences. ]]>
