CodeVetter · Repo Unpack · Risk map
yt-dlp/yt-dlp
Highest-sensitivity areas: the self-updater downloads and replaces the running executable from GitHub releases (sha256-verified but not signature-verified); `ExecPP` and external downloaders shell out to user-supplied commands; `cookies.py` decrypts browser credential stores; the plugin system executes arbitrary code from config dirs by default; and `jsinterp.py`/ejs remote components execute third-party JavaScript. Fragility concentrates in the ~1752 regex-driven extractors and the first-match-wins registry ordering. Note the repo carries an explicit NO AI / NO LLM contribution policy.
- POLICY: `.NO_AI/README.md` states the project strictly forbids LLM/AI contributions of any kind (code, issues, comments, translations) and instructs LLM agents to refuse interaction; CONTRIBUTING.md repeats it. Any agent must surface this before producing patches intended for upstream.
- `ExecPP.run` executes commands via `Popen.run(cmd, shell=True)`; template expansion is safety-validated at `set_downloader` unless `allow-unsafe-exec-expansion` is in compat_opts — a deliberate but real command-injection surface when combined with attacker-controlled metadata.
- Self-update downloads a replacement binary over the network and swaps it in-place; integrity relies on a sha256 checksum from the same release (SHA2-256SUMS) rather than a signature, and when the checksum is missing restart is only blocked, not the install itself.
- Windows update cleanup shells out: `atexit.register(Popen, 'ping 127.0.0.1 -n 5 -w 1000 & del /F ...', shell=True)` to delete the old binary after exit.
- Plugins execute arbitrary Python from user/system config dirs, `yt-dlp-plugins` dirs, and PYTHONPATH — enabled by default (`plugin_dirs=['default']`) unless `YTDLP_NO_PLUGINS` is set; the API explicitly disclaims backwards compatibility.
- `cookies.py` reads and decrypts browser cookie databases (sqlite copies, AES-GCM/CBC via `aes.py`, OS keyrings via secretstorage/dbus, subprocess calls) — high-severity credential-handling code that reads outside the repo.
- `file://` URLs are refused by default for security and only enabled via `--enable-file-urls`; the special-case is handled in `urlopen` by inspecting `NoSupportingHandlers` reasons.
- Extractor fragility: ~1752 `_VALID_URL` regexes are evaluated in registry order (first match wins; YouTube hoisted first, `GenericIE` with `_VALID_URL='.*'` last), so adding or reordering an extractor can silently change which IE handles a URL.
- Remote JS execution: `--remote-components ejs:github`/`ejs:npm` downloads solver code fetched at runtime, and `jsinterp.py` interprets remote JavaScript for signature deciphering — remote-code supply-chain surface gated behind explicit options.
- ffmpeg invocation builds argv programmatically and forces `file:`-prefixed filename arguments to defend against ffmpeg protocol tricks — the comment documents the attack class (filenames containing ':' or starting with '-').
- Mutable global `Indirect` registries mean plugin classes are merged ahead of built-ins (`merge_dicts(regular_classes, destination)`), so a plugin can shadow a core extractor/postprocessor and behavior depends on load order. (inferred)
- Core CI excludes `yt_dlp/extractor/**` changes (except youtube/common/extractors), and download tests hit live third-party sites — so most extractor regressions are only caught by the flaky download tier, not the core gate. (inferred)