From 101461b97c141d014b86f994fd1804b771c0a804 Mon Sep 17 00:00:00 2001 From: NimBold Date: Sat, 22 Aug 2026 02:56:21 +0330 Subject: [PATCH] fix(media): harden bundled engine ownership - Fail closed on CWD engine fallback in release builds. - Redact media process diagnostics and remove unused Aria2 resolution. - Add release-mode resolver regression coverage and rerun media gates. --- src-tauri/src/engines.rs | 47 +++++++++++++++++++++++++++++++++++----- src-tauri/src/lib.rs | 21 ++++++++++-------- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/engines.rs b/src-tauri/src/engines.rs index 88d7e65..5da82d8 100644 --- a/src-tauri/src/engines.rs +++ b/src-tauri/src/engines.rs @@ -11,7 +11,7 @@ pub fn resolve_bundled_binary_path( if let Ok(resource_dir) = app_handle.path().resource_dir() { for candidate in packaged_candidates(&resource_dir, &target, &binary_name) { if candidate.is_file() { - log::info!("Resolved bundled '{}' at: {:?}", engine, candidate); + log::info!("Resolved bundled '{}' for target '{}'", engine, target); return Ok(candidate); } } @@ -20,19 +20,24 @@ pub fn resolve_bundled_binary_path( if let Ok(exe_path) = std::env::current_exe() { for candidate in executable_relative_candidates(&exe_path, &target, &binary_name) { if candidate.is_file() { - log::info!("Resolved bundled '{}' at: {:?}", engine, candidate); + log::info!("Resolved bundled '{}' for target '{}'", engine, target); return Ok(candidate); } } } + // Development payloads are intentionally discoverable from the checkout, + // but a packaged/release app must never execute an engine selected by its + // working directory. If the packaged resource or executable-relative + // payload is missing, fail closed instead of allowing a same-named binary + // from an untrusted CWD to take over the media/download process. if let Ok(cwd) = std::env::current_dir() { - for candidate in development_candidates(&cwd, &target, &binary_name) { + for candidate in development_candidates_for_runtime(&cwd, &target, &binary_name) { if candidate.is_file() { let absolute = candidate.canonicalize().map_err(|error| { format!("Failed to canonicalize '{}': {error}", candidate.display()) })?; - log::info!("Resolved bundled '{}' at: {:?}", engine, absolute); + log::info!("Resolved bundled '{}' for target '{}'", engine, target); return Ok(absolute); } } @@ -44,6 +49,22 @@ pub fn resolve_bundled_binary_path( )) } +fn development_candidates_for_runtime( + cwd: &Path, + target: &str, + binary_name: &str, +) -> Vec { + #[cfg(debug_assertions)] + { + development_candidates(cwd, target, binary_name) + } + #[cfg(not(debug_assertions))] + { + let _ = (cwd, target, binary_name); + Vec::new() + } +} + fn packaged_candidates(resource_dir: &Path, target: &str, binary_name: &str) -> Vec { let mut candidates = vec![ resource_dir @@ -98,6 +119,7 @@ fn executable_relative_candidates( candidates } +#[cfg(any(debug_assertions, test))] fn development_candidates(cwd: &Path, target: &str, binary_name: &str) -> Vec { let roots = [cwd.to_path_buf(), cwd.join("src-tauri")]; let mut candidates = Vec::new(); @@ -141,7 +163,7 @@ fn aria2_openssl_modules_dir(binary_path: &Path) -> Option { #[cfg(test)] mod tests { - use super::{development_candidates, packaged_candidates}; + use super::{development_candidates, development_candidates_for_runtime, packaged_candidates}; use std::path::Path; #[test] @@ -171,4 +193,19 @@ mod tests { Path::new("/repo/engine-dist/x86_64-pc-windows-msvc/aria2c-x86_64-pc-windows-msvc.exe") ); } + + #[test] + fn development_resolution_is_disabled_in_release_builds() { + let candidates = development_candidates_for_runtime( + Path::new("/repo"), + "x86_64-unknown-linux-gnu", + "yt-dlp-x86_64-unknown-linux-gnu", + ); + + if cfg!(debug_assertions) { + assert!(!candidates.is_empty()); + } else { + assert!(candidates.is_empty()); + } + } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index eb597d7..8169743 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4754,12 +4754,10 @@ pub(crate) async fn start_media_download_internal( let mut progress_state = MediaProgressEmitterState::new(); // Resolve absolute paths to bundled binaries - let aria2c_path = resolve_bundled_binary_path(&app_handle, "aria2c")?; let ffmpeg_path = resolve_bundled_binary_path(&app_handle, "ffmpeg")?; let deno_path = resolve_bundled_binary_path(&app_handle, "deno")?; - log::info!("Using bundled aria2c: {:?}", aria2c_path); - log::info!("Using bundled ffmpeg: {:?}", ffmpeg_path); - log::info!("Using bundled deno: {:?}", deno_path); + log::info!("Using bundled FFmpeg engine"); + log::info!("Using bundled Deno engine"); // yt-dlp accepts an absolute path for its external downloader. Keep every // engine explicit so behavior never depends on PATH, symlink privileges, @@ -4982,13 +4980,18 @@ pub(crate) async fn start_media_download_internal( } let lower = line.to_lowercase(); if lower.contains("error") || lower.contains("critical") { - log::error!("yt-dlp stderr [{}]: {}", id, line.trim()); + log::error!( + "yt-dlp stderr [{}]: {}", + id, + redact_log_line_for_app(line.trim(), &app_handle) + ); } } } Some(tauri_plugin_shell::process::CommandEvent::Error(err)) => { - log::error!("yt-dlp shell error [{}]: {}", id, err); - break err; + let safe_error = redact_log_line_for_app(&err, &app_handle); + log::error!("yt-dlp shell error [{}]: {}", id, safe_error); + break safe_error; } Some(tauri_plugin_shell::process::CommandEvent::Terminated(payload)) => { if let Some(line) = flush_media_output_line(&mut stdout_buffer) { @@ -5065,7 +5068,7 @@ pub(crate) async fn start_media_download_internal( break if stderr_tail.is_empty() { format!("yt-dlp exited with code {:?}", payload.code) } else { - stderr_tail.clone() + redact_log_line_for_app(&stderr_tail, &app_handle) }; } Some(_) => {} @@ -5073,7 +5076,7 @@ pub(crate) async fn start_media_download_internal( break if stderr_tail.is_empty() { "yt-dlp process ended unexpectedly".to_string() } else { - stderr_tail.clone() + redact_log_line_for_app(&stderr_tail, &app_handle) }; } }