From 821bef0f00178916d60dbc86bc0bcb8cc3bae8d5 Mon Sep 17 00:00:00 2001 From: Simon Sawicki Date: Wed, 10 Jun 2026 01:01:32 +0200 Subject: [PATCH] [cleanup] Misc (#16697) Authored by: Grub4K, bashonly Co-authored-by: bashonly --- devscripts/changelog_override.json | 25 +++++++++++++++++++++++++ devscripts/update_requirements.py | 4 ++-- test/test_YoutubeDL.py | 4 ++-- test/test_downloader_external.py | 1 + test/test_execution.py | 7 ++++++- yt_dlp/YoutubeDL.py | 16 +++++++++++++--- yt_dlp/downloader/external.py | 6 ++---- 7 files changed, 51 insertions(+), 12 deletions(-) diff --git a/devscripts/changelog_override.json b/devscripts/changelog_override.json index 937a8880bc..7ff3a6caa0 100644 --- a/devscripts/changelog_override.json +++ b/devscripts/changelog_override.json @@ -342,5 +342,30 @@ "action": "add", "when": "1fbbe29b99dc61375bf6d786f824d9fcf6ea9c1a", "short": "[priority] Security: [[CVE-2026-26331](https://nvd.nist.gov/vuln/detail/CVE-2026-26331)] [Arbitrary command injection with the `--netrc-cmd` option](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-g3gw-q23r-pgqm)\n - The argument passed to the command in `--netrc-cmd` is now limited to a safe subset of characters" + }, + { + "action": "add", + "when": "98e42eb04486e00bf86479b24dbfe19321f652ee", + "short": "[priority] **The minimum supported versions of Deno, Node, and Bun have been raised.**\n The minimum required version of [Deno](https://github.com/yt-dlp/yt-dlp/issues/16767) is now `v2.3.0`; supported [Node](https://github.com/yt-dlp/yt-dlp/issues/16765) versions are `v22` and up; [Bun support has been deprecated](https://github.com/yt-dlp/yt-dlp/issues/16766) and limited to versions `1.2.11` through `1.3.14`." + }, + { + "action": "add", + "when": "5faffa999fd33b373d47773e8ee639d072accec2", + "short": "[priority] Security: Usage of vulnerable conversions (e.g. `%()s`) with the `--exec` option is an all-too-common pitfall. To remedy this, `--exec` now only allows safe conversions in its command templates.\n - Most users can simply replace `%(...)s` with `%(...)q` in their `--exec` argument(s). Numeric conversions are unaffected by this change. Using unsafe conversions with `--exec` poses a significant security risk. [Read more]()" + }, + { + "action": "add", + "when": "e578e265f7c6ca94a74b30e0d8d6196a4d19fb6a", + "short": "[priority] Security: [[CVE-2026-50023](https://nvd.nist.gov/vuln/detail/CVE-2026-50023)] [Dangerous file type creation via insufficient filename sanitization](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-c6mh-fpjc-4pr3)\n - Writing files with the extensions `.desktop`, `.url`, or `.webloc` is now only allowed in the context of `--write-link` functionality" + }, + { + "action": "add", + "when": "2726572520238356bcf64aba2040228648b44c82", + "short": "[priority] Security: [[CVE-2026-50019](https://nvd.nist.gov/vuln/detail/CVE-2026-50019)] [File Downloader cookie leak with curl](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-f7j3-774f-rfhj)\n - Impact is limited to users of `--downloader curl`; cookies are now properly passed to curl so that it respects their scope" + }, + { + "action": "add", + "when": "25056f0d2d47adbd235a8d422fa62d68d0be2bc2", + "short": "[priority] Security: [[CVE-2026-50574](https://nvd.nist.gov/vuln/detail/CVE-2026-50574)] [Arbitrary code execution via manifest downloads with aria2c](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-vx4q-3cr2-7cg2)\n - Impact is limited to users of `--downloader aria2c`\n - Support for downloading HLS and DASH formats with aria2c has been removed. Users affected by this change should migrate to use `-N` for concurrent fragment downloads via the native downloader" } ] diff --git a/devscripts/update_requirements.py b/devscripts/update_requirements.py index 7231e8bdaf..23676c9450 100755 --- a/devscripts/update_requirements.py +++ b/devscripts/update_requirements.py @@ -650,8 +650,8 @@ def update_requirements( modify_and_write_pyproject(pyproject_text, table_name=EXTRAS_TABLE, table=extras) # Generate/upgrade final lockfile that includes pinned extras - print(f'Running: uv lock {upgrade_arg}', file=sys.stderr) - run_process('uv', 'lock', upgrade_arg, env=env) + print('Running: uv lock', file=sys.stderr) + run_process('uv', 'lock', env=env) # Export bundle requirements; any updates to these are already recorded w/ uv.lock package diff for target_suffix, target in BUNDLE_TARGETS.items(): diff --git a/test/test_YoutubeDL.py b/test/test_YoutubeDL.py index 2705accb76..0f84d7e169 100644 --- a/test/test_YoutubeDL.py +++ b/test/test_YoutubeDL.py @@ -6,11 +6,11 @@ import sys import unittest from unittest.mock import patch -from yt_dlp.globals import all_plugins_loaded - sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from yt_dlp.globals import all_plugins_loaded + import contextlib import copy import json diff --git a/test/test_downloader_external.py b/test/test_downloader_external.py index 9db485af9b..0b51c96173 100644 --- a/test/test_downloader_external.py +++ b/test/test_downloader_external.py @@ -102,6 +102,7 @@ class HTTPTestHandler(http.server.BaseHTTPRequestHandler): self.end_headers() self.wfile.write(json.dumps(list(self.headers.items())).encode()) + class HTTPTestServer(http.server.HTTPServer): @property def address(self, /): diff --git a/test/test_execution.py b/test/test_execution.py index c6ee9cf9d0..cd587fe334 100644 --- a/test/test_execution.py +++ b/test/test_execution.py @@ -20,7 +20,12 @@ LAZY_EXTRACTORS = 'yt_dlp/extractor/lazy_extractors.py' class TestExecution(unittest.TestCase): def run_yt_dlp(self, exe=(sys.executable, 'yt_dlp/__main__.py'), opts=('--version', )): stdout, stderr, returncode = Popen.run( - [*exe, '--ignore-config', *opts], cwd=rootDir, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [*exe, '--no-update', '--ignore-config', *opts], + cwd=rootDir, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) print(stderr, file=sys.stderr) self.assertEqual(returncode, 0) return stdout.strip(), stderr.strip() diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 47ed811c64..6e7ae2a9ee 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -1313,6 +1313,7 @@ class YoutubeDL: )$''') SAFE_EXEC_CONVERSIONS = 'difq' UNSAFE_DEFAULT_CHARS = '"\' \n\t;&|^$%*<>{}()[]`#\\' + EXEC_ADVISORY_MSG = 'See https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-69qj-pvh9-c5wg for details' def _from_user_input(field): if field == ':': @@ -1440,12 +1441,21 @@ class YoutubeDL: # Validate safety of exec commands if _exec: if fmt[-1] not in SAFE_EXEC_CONVERSIONS: - raise UnsafeExecExpansionError(f'Unsafe conversion(s) in exec command: {outtmpl!r}') + raise UnsafeExecExpansionError( + f'Unsafe conversion(s) in exec command: {outtmpl!r}\n' + f'Conversions such as %()s are too dangerous to be used in ' + f'--exec command templates; use %()q instead. {EXEC_ADVISORY_MSG}') elif any(unsafe_char in default for unsafe_char in UNSAFE_DEFAULT_CHARS): if default == na: - raise UnsafeExecExpansionError(f'Unsafe placeholder for exec command: {na!r}') + raise UnsafeExecExpansionError( + f'Unsafe placeholder for exec command: {na!r}\n' + f'The --output-na-placeholder argument also applies to ' + f'--exec command templates. {EXEC_ADVISORY_MSG}') else: - raise UnsafeExecExpansionError(f'Unsafe default(s) in exec command: {outtmpl!r}') + raise UnsafeExecExpansionError( + f'Unsafe default(s) in exec command: {outtmpl!r}\n' + f'Conversions are not applied to --exec command template defaults, ' + f'e.g. %(...|DEFAULT;)q. {EXEC_ADVISORY_MSG}') flags = outer_mobj.group('conversion') or '' str_fmt = f'{fmt[:-1]}s' diff --git a/yt_dlp/downloader/external.py b/yt_dlp/downloader/external.py index 37a42af91a..2f127c0f87 100644 --- a/yt_dlp/downloader/external.py +++ b/yt_dlp/downloader/external.py @@ -11,6 +11,7 @@ import time from .fragment import FragmentFD from ..postprocessor.ffmpeg import EXT_TO_OUT_FORMATS, FFmpegPostProcessor from ..utils import ( + DownloadError, Popen, RetryManager, _configuration_args, @@ -136,8 +137,6 @@ class ExternalFD(FragmentFD): self.to_screen(f'[download] Writing temporary cookies file to "{self._cookies_tempfile}"') # real_download resets _cookies_tempfile; if it's None then save() will write to cookiejar.filename self.ydl.cookiejar.save(self._cookies_tempfile, True, True) - with open(self.ydl.cookiejar.filename or self._cookies_tempfile, "r") as file: - print("cookies", repr(file.read())) return self.ydl.cookiejar.filename or self._cookies_tempfile def _call_downloader(self, tmpfilename, info_dict): @@ -224,8 +223,7 @@ class CurlFD(ExternalFD): else: cookies_file = self._write_cookies() if '=' in cookies_file: - # XXX: what to raise here? - raise RuntimeError('curl version too old or temp directory contains `=`; please use another downloader or update curl') + raise DownloadError('curl version too old or temp directory contains `=`; please use another downloader or update curl') assert cookies_file != '-' cmd += ['--cookie', cookies_file]