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
+12 -7
View File
@@ -112,6 +112,10 @@ class DownloadYtdlpThread(QThread):
try:
# Extra logic moved to src\utils\ytsage_constants.py
exe_path = YTDLP_APP_BIN_PATH
# Download to a temp name and only move to the trusted path after
# the hash checks out, so a crash mid-download can never leave an
# unverified binary where the app will execute it.
part_path = exe_path.with_name(exe_path.name + ".part")
# Download with progress reporting
logger.info(f"Downloading yt-dlp from: {YTDLP_DOWNLOAD_URL}")
@@ -123,7 +127,7 @@ class DownloadYtdlpThread(QThread):
if total_size == 0:
self.progress_signal.emit(100)
with open(exe_path, "wb") as f:
with open(part_path, "wb") as f:
downloaded = 0
for data in response.iter_content(block_size):
f.write(data)
@@ -133,22 +137,23 @@ class DownloadYtdlpThread(QThread):
self.progress_signal.emit(progress)
logger.info("Download complete, verifying SHA256 hash...")
# Verify SHA256 hash
if not verify_ytdlp_sha256(exe_path, YTDLP_DOWNLOAD_URL):
if not verify_ytdlp_sha256(part_path, YTDLP_DOWNLOAD_URL):
# Hash verification failed - delete the downloaded file
logger.error("SHA256 verification failed! Removing downloaded file.")
if Path(exe_path).exists():
Path(exe_path).unlink()
part_path.unlink(missing_ok=True)
self.finished_signal.emit(
False,
False,
"SHA256 verification failed. The downloaded file may be corrupted or tampered with."
)
return
# Make executable on macOS and Linux
if OS_NAME != "Windows":
os.chmod(exe_path, 0o755)
os.chmod(part_path, 0o755)
os.replace(part_path, exe_path)
logger.info("yt-dlp downloaded and verified successfully!")
self.finished_signal.emit(True, str(exe_path))