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.
This commit is contained in:
NimBold
2026-08-22 02:56:21 +03:30
parent a55cdee7e5
commit 101461b97c
2 changed files with 54 additions and 14 deletions
+42 -5
View File
@@ -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<PathBuf> {
#[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<PathBuf> {
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<PathBuf> {
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<PathBuf> {
#[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());
}
}
}
+12 -9
View File
@@ -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)
};
}
}