CodeVetter · Repo Unpack · Risk map
ytdl-org/youtube-dl
The highest-risk areas are shell-outs (postprocessor --exec with shell=True, external downloader and ffmpeg command construction), credential handling (cookies, netrc, passwords), the self-update path that replaces the running binary, and the eval-adjacent JS/SWF interpreters. The codebase also carries heavy legacy weight: Python 2.6 compat shims, dead modules (swfinterp is only referenced by tests), and fragile regex-based scraping across ~800 site extractors that break silently when sites change.
- ExecAfterDownloadPP.run executes the user's --exec command with subprocess.call(shell=True); the {} placeholder is shlex-quoted, but the rest of the command string is raw shell — a deliberate but real command-execution surface.
- External downloaders and ffmpeg are spawned via subprocess.Popen with argv built from info_dict fields (url, http_headers, cookies); cookies are serialized into temp files or -cookies/-headers args, so a malicious extractor result could influence command lines, though lists (not shell strings) are used.
- Self-update replaces the running executable with a network download; it is gated by an RSA signature over versions.json plus a per-file sha256 check, but trusts a hard-coded RSA key and yt-dl.org availability; the Windows path writes and spawns a .bat file that swaps the exe.
- Cookie leak mitigations indicate a history of security issues: _remove_cookie_header strips Cookie from http_headers, _load_cookies scopes header-supplied cookies to the target domain (referencing yt-dlp advisory GHSA-v8mc-9377-rwjj), and YoutubeDLRedirectHandler drops Cookie on redirect; the file:// scheme is explicitly disabled citing issue #8227.
- _UnsafeExtensionError enforces an extension whitelist on output files to prevent path traversal/arbitrary file overwrite (referencing yt-dlp advisory GHSA-79w7-vh3h-8g4j); --no-check-extensions sets the lenient flag to bypass it.
- Aria2pFD connects to an aria2 daemon at http://localhost:6800 with an empty RPC secret and trusts the aria2p library being importable — local RPC without authentication.
- JSInterpreter is a partial JS engine that executes extracted player code semantics; _extract_sig_fn even splices a regex-extracted 'useful values' snippet into the interpreted function — a YouTube player change silently breaks decryption, and malformed code raises JSInterpreter.Exception.
- swfinterp.py (834 lines, SWFInterpreter) is effectively dead production code — the only importer in the tree is test/test_swfinterp.py; PhantomJSwrapper in extractor/openload.py survives only as a dependency of pornhub.py and a debug-header version probe.
- Extractors are regex-fragile by design: _search_regex raises RegexNotFoundError on missing patterns and CONTRIBUTING.md explicitly warns that extractor breakage on site changes is expected; broken extractors are flagged via the _WORKING class attribute rather than removed.
- --call-home/--verbose debug path sends requests to https://yt-dl.org/ip and /latest/version, disclosing the user's public IP to the project's own server.
- Python 2.6 support is still load-bearing: compat.py shims, _workaround_optparse_bug9161, py2exe paths, and CI builds Python 2.6/2.7 from source — code paths that cannot be exercised on modern interpreters and constrain idioms (no f-strings, unicode_literals everywhere). (inferred)
- Passwords entered interactively (compat_getpass) or passed via -p/--password appear in argv and config files; _hide_login_info only scrubs them from verbose debug echoes, not from process listings or config storage. (inferred)