T09 · Insecure Skill Coding Practices
Warning
- Location
- yt_transcript.py:13
- Finding
- Unrestricted URL Enables Unintended Network Requests<![CDATA[ ## Vulnerability Details **File Location**: `yt_transcript.py:13-27` **Vulnerability Type**: Server-Side Request Forgery / Unrestricted Network Target **Risk Level**: Medium ### Vulnerable Code ```python def download_subs(url: str, lang: str = "en") -> str: """Download auto-generated subtitles and return the VTT content.""" with tempfile.TemporaryDirectory() as tmp: out = Path(tmp) / "sub" subprocess.run( [ "yt-dlp", "--write-auto-sub", "--write-sub", "--skip-download", "--sub-lang", lang, "-o", str(out), url, ], check=True, capture_output=True, text=True, ) ``` ### Technical Analysis The command-line URL is passed directly to the network-capable `yt-dlp` program without validating its scheme, hostname, port, or resolved destination. Although the Skill is documented as accepting YouTube URLs, the implementation does not enforce that restriction. `subprocess.run` uses an argument list and does not enable `shell=True`, so this is not a shell-command injection vulnerability. The risk instead arises because `yt-dlp` supports multiple websites and generic URL extraction. An attacker who can influence the URL given to the Agent may cause the host running the Skill to contact a destination outside the declared YouTube service. This could include attacker-controlled servers or services reachable only from the Agent's network environment. Redirect behavior may also undermine a hostname-only check unless redirect destinations and resolved addresses are constrained. ### Attack Path 1. An attacker supplies a crafted non-YouTube URL while requesting transcript extraction. 2. The Agent follows the documented workflow and invokes `yt_transcript.py` with that URL. 3. `download_subs()` forwards the URL to `yt-dlp` without validation. 4. `yt-dlp` attempts to a ...[truncated 941 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse the input with `urllib.parse.urlparse` before invoking `yt-dlp`. 2. Require the `https` scheme and reject URLs containing embedded credentials. 3. Allowlist the intended YouTube hostnames, such as: - `youtube.com` - `www.youtube.com` - `m.youtube.com` - `youtu.be` 4. Normalize hostnames before comparison and use exact-name or controlled-subdomain matching rather than substring checks. 5. Resolve the destination and reject loopback, link-local, private, multicast, reserved, and unspecified IP address ranges. 6. Account for DNS rebinding and redirects by validating the destination after resolution and constraining redirect targets where supported. 7. Run the downloader in a sandbox with egress restricted to required YouTube endpoints. 8. Apply execution timeouts and resource limits to reduce denial-of-service exposure. Example initial validation: ```python from urllib.parse import urlparse ALLOWED_HOSTS = { "youtube.com", "www.youtube.com", "m.youtube.com", "youtu.be", } def validate_youtube_url(value: str) -> str: parsed = urlparse(value) host = (parsed.hostname or "").lower().rstrip(".") if parsed.scheme != "https" or host not in ALLOWED_HOSTS: raise ValueError("Only HTTPS YouTube URLs are permitted") if parsed.username is not None or parsed.password is not None: raise ValueError("URL credentials are not permitted") return value ``` ]]>
