T09 · Insecure Skill Coding Practices
Error
- Location
- lib/frame-extractor.js:29
- Finding
- Shell Command Injection Through Untrusted Paths and URLs<![CDATA[ ## Vulnerability Details **File Location**: `lib/frame-extractor.js:29-31, 92-96`; `lib/audio-processor.js:14-19, 40-42, 58-60`; `lib/video-downloader.js:66-68` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js // lib/frame-extractor.js const { stdout } = await execAsync( `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${videoPath}"` ); const fps = 1 / actualInterval; const command = `ffmpeg -i "${videoPath}" -vf "fps=${fps},scale='min(1280,iw)':-1" -q:v 2 "${framePattern}" -y`; console.log(` 🔄 正在提取关键帧...`); await execAsync(command); ``` ```js // lib/audio-processor.js const finalAudioPath = `${audioPathRaw}.wav`; return new Promise((resolve, reject) => { const cmd = `ffmpeg -i "${videoPath}" -vn -acodec pcm_s16le -ac 1 -ar 16000 -y "${finalAudioPath}"`; exec(cmd, (error) => { if (error) reject(error); else resolve(finalAudioPath); }); }); const getDurationCmd = `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${audioPath}"`; const duration = await new Promise((resolve) => { exec(getDurationCmd, (err, stdout) => resolve(parseFloat(stdout) || 0)); }); const cutCmd = `ffmpeg -ss ${startTime} -t ${SEGMENT_DURATION} -i "${audioPath}" -acodec copy -y "${segmentPath}"`; exec(cutCmd, (err) => err ? reject(err) : resolve()); ``` ```js // lib/video-downloader.js const command = `yt-dlp -o "${outputPath}" --no-warnings "${videoUrl}" 2>&1`; const { stdout, stderr } = await execAsync(command, { timeout: 120000 }); ``` ### Technical Analysis The Skill constructs shell command strings by interpolating local file paths, generated output paths, and browser-derived media URLs, then invokes them through `child_process.exec`. This API executes commands through a system shell. Wrapping a value in double quotes does not make it safe for shell execution. An input containing an embedded quote can terminate the quoted argum ...[truncated 1498 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace every `exec`/promisified `exec` invocation with `execFile` or `spawn` and an argument array: ```js await execFileAsync('ffprobe', [ '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', videoPath ]); ``` 2. Invoke FFmpeg and yt-dlp with `shell: false`; never concatenate paths or URLs into a command string. 3. Validate media URLs with the `URL` API and allow only expected protocols and hosts. 4. Generate output identifiers locally from a restricted character set rather than incorporating remote identifiers directly. 5. Add regression tests using filenames and URLs containing quotes, command substitutions, semicolons, newlines, and platform-specific shell metacharacters. 6. Run media-processing tools under a dedicated unprivileged account or container with restricted filesystem and network access. ]]>
