T09 · Insecure Skill Coding Practices
- Location
src/cli.js:28- Finding
Arbitrary Command Execution Through Unsafely Interpolated Video URL
- Content
View full analysis
Vulnerability Details
File Location:
src/cli.js, lines 28-35 and 45-52
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
js function getVideoInfo(url) { try { const output = execSync(`yt-dlp --dump-json --no-download "${url}"`, { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] }); return JSON.parse(output);js try { // Try auto-generated first, then manual subs execSync(`yt-dlp --write-auto-sub --sub-lang en --skip-download --sub-format vtt -o "${tmpFile}" "${url}"`, { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] });Technical Analysis
The application inserts the user-controlled
urldirectly into shell command strings passed toexecSync(). Because string-formexecSync()invokes a shell, shell metacharacters contained in the URL are interpreted as command syntax rather than as part of a singleyt-dlpargument.The preceding
extractVideoId()validation does not prevent exploitation. Its YouTube URL regular expression is not anchored to the end of the input and only needs to find a matching URL prefix. Consequently, an input can begin with a valid-looking YouTube URL and contain a closing quote, command separators, and an additional command afterward. Although the extracted video ID is used to decide whether the input is acceptable, the original untrustedurlis subsequently passed to the shell.Both the video-information and transcript retrieval paths contain this unsafe command construction, so all supported commands can reach at least one vulnerable sink after dependency validation succeeds.
Attack Path
- The attacker persuades a user or automated process to invoke the CLI with a crafted URL.
- The supplied value starts with a URL accepted by
extractVideoId(), such as a YouTube watch URL containing an apparent video identif ...[truncated 1358 chars]
- Remediation
View remediation
Remediation Suggestions
-
Replace shell-string execution with an API that passes arguments separately and disables shell interpretation. For example:
js import { execFileSync } from 'child_process'; const output = execFileSync( 'yt-dlp', ['--dump-json', '--no-download', url], { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] } );Apply the same pattern to transcript retrieval:
js execFileSync( 'yt-dlp', [ '--write-auto-sub', '--sub-lang', 'en', '--skip-download', '--sub-format', 'vtt', '-o', tmpFile, url ], { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] } ); -
Do not attempt to make string-form shell execution safe solely through quoting or character replacement. Argument-array execution with no shell should be the primary control.
-
Parse input with the standard
URLclass and enforce an explicit host allowlist such asyoutube.com,www.youtube.com, andyoutu.be. Validate the expected pathname and require the video identifier to match the complete expected 11-character format. -
Reject malformed URLs, embedded control characters, unexpected credentials, and unsupported protocols before invoking external tools.
-
Add regression tests using URLs containing quotes, semicolons, command substitutions, newlines, and other shell metacharacters. Tests should confirm that these values are rejected or passed only as inert arguments.
-
Run the CLI with least privilege and avoid executing it under administrative accounts or highly privileged automation identities.
-
