Enforce SHA256 verification on every binary install path

Three gaps allowed an unverified binary to reach a trusted location:

- The ffmpeg ZIP fallback logged a warning on checksum mismatch and
  installed anyway (the 7z path already aborted). Abort instead.
- The yt-dlp auto-update path downloaded and renamed the binary over
  the verified one with no checksum at all. Verify against the official
  SHA2-256SUMS like the first-install path, and use atomic os.replace.
- The yt-dlp first install streamed the download directly to the
  trusted path and only verified afterwards; a crash in between left an
  unverified executable to be run on next launch. Download to .part and
  os.replace only after verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-07-25 01:24:35 +02:00
parent b40deb0d5f
commit dff14ec3e8
3 changed files with 33 additions and 16 deletions
+14 -7
View File
@@ -463,7 +463,7 @@ def update_yt_dlp() -> bool:
# Download the latest version
try:
response = requests.get(YTDLP_DOWNLOAD_URL, stream=True)
response = requests.get(YTDLP_DOWNLOAD_URL, stream=True, timeout=60)
if response.status_code == 200:
# Create a temporary file
temp_file = f"{yt_dlp_path}.new"
@@ -472,21 +472,28 @@ def update_yt_dlp() -> bool:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
# Verify against the official checksums before touching the
# trusted binary (same check as the first-install path)
from ytsage.core.ytsage_yt_dlp import verify_ytdlp_sha256
if not verify_ytdlp_sha256(Path(temp_file), YTDLP_DOWNLOAD_URL):
logger.error("SHA256 verification failed for yt-dlp update, keeping current binary")
Path(temp_file).unlink(missing_ok=True)
return False
# Make executable on Unix systems
if OS_NAME != "Windows":
os.chmod(temp_file, 0o755)
# Replace the old file with the new one
# Replace the old file with the new one (os.replace is atomic
# and overwrites on Windows too)
try:
# On Windows, we need to remove the old file first
if OS_NAME == "Windows" and yt_dlp_path.exists():
yt_dlp_path.unlink(missing_ok=True)
Path(temp_file).rename(yt_dlp_path)
os.replace(temp_file, yt_dlp_path)
logger.info("yt-dlp binary successfully updated")
return True
except Exception as e:
logger.exception(f"Error replacing yt-dlp binary: {e}")
Path(temp_file).unlink(missing_ok=True)
return False
else:
logger.info(f"Failed to download latest yt-dlp: HTTP {response.status_code}")