T09 · Insecure Skill Coding Practices
- Location
scripts/download.py:47- Finding
Unrestricted URL Processing Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/download.py, lines 7–11 and 47–65
Vulnerability Type: Server-Side Request Forgery (SSRF) and unsafe command argument handling
Risk Level: MediumVulnerable Code
python def get_formats(url): try: # Use --no-playlist to avoid downloading entire playlists # Use --quiet to reduce noise result = subprocess.run(['yt-dlp', '-j', '--no-playlist', url], capture_output=True, text=True)python def download_video(url, format_id, output_filename): try: # Sanitize filename (remove non-alphanumeric except dots/dashes) safe_name = re.sub(r'[^a-zA-Z0-9._-]', '_', output_filename) if not safe_name.endswith('.mp4'): safe_name += '.mp4' # Download command # We try to merge into mp4 for maximum compatibility cmd = [ 'yt-dlp', '-f', f'{format_id}+bestaudio/best', '--merge-output-format', 'mp4', '--no-playlist', '-o', safe_name, url ] result = subprocess.run(cmd, capture_output=True, text=True)Technical Analysis
The
urlvalue is taken directly fromsys.argv[2]and passed to the network-capableyt-dlpprocess without validating its scheme, destination hostname, resolved IP address, or redirect targets. Becauseyt-dlpsupports generic URLs in addition to public video platforms, an attacker can supply URLs that reference services reachable only from the host running the Skill.No controls reject loopback, private, link-local, reserved, or otherwise non-public IPv4 and IPv6 destinations. Redirects are also not independently validated, so a public URL could redirect the request to an internal service. DNS rebinding may similarly defeat a validation mechanism that checks only the original hostname.
The URL is passed aft ...[truncated 1920 chars]
- Remediation
View remediation
Remediation Suggestions
-
Parse the supplied URL before invoking
yt-dlpand permit only explicitly supported schemes, preferablyhttpsand, if required,http. -
Prefer an explicit allowlist of trusted public video-platform domains instead of accepting arbitrary sites.
-
Resolve the destination hostname and reject every address in loopback, private, link-local, multicast, unspecified, reserved, and other non-public ranges for both IPv4 and IPv6.
-
Apply the same validation to every redirect destination. Do not rely solely on validation of the original URL.
-
Protect against DNS rebinding by ensuring that the address actually used for the connection remains an approved public address.
-
Insert
--immediately before the URL argument so that dash-prefixed values cannot be interpreted asyt-dlpoptions:python result = subprocess.run( ['yt-dlp', '-j', '--no-playlist', '--', url], capture_output=True, text=True, timeout=60 )Apply the same option terminator to the download command.
-
Add execution timeouts, maximum download-size limits, rate limits, and output quotas to reduce denial-of-service exposure.
-
Run the downloader in a sandbox with restricted filesystem access and outbound network access limited to approved public destinations.
-
Validate
format_idagainst the format identifiers returned by the preceding information request rather than accepting an arbitrary value.
-
