From 27f700e2b2afce4c150f641dc0f101922e198c42 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 13 Nov 2025 14:37:47 +0100 Subject: [PATCH] askpass: Quote paths in generated askpass script (#42622) Closes https://github.com/zed-industries/zed/issues/42618 Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/askpass/src/askpass.rs | 56 +++++++++++++++++++++--------- crates/remote/src/transport/ssh.rs | 2 +- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/crates/askpass/src/askpass.rs b/crates/askpass/src/askpass.rs index 81cdd355bf7173b3954a8c2731a0728d354253ba..0974409477d452958df13893e316845a919723c5 100644 --- a/crates/askpass/src/askpass.rs +++ b/crates/askpass/src/askpass.rs @@ -205,13 +205,9 @@ impl PasswordProxy { } else { ShellKind::Posix }; - let askpass_program = ASKPASS_PROGRAM - .get_or_init(|| current_exec) - .try_shell_safe(shell_kind) - .context("Failed to shell-escape Askpass program path.")? - .to_string(); + let askpass_program = ASKPASS_PROGRAM.get_or_init(|| current_exec); // Create an askpass script that communicates back to this process. - let askpass_script = generate_askpass_script(&askpass_program, &askpass_socket); + let askpass_script = generate_askpass_script(shell_kind, askpass_program, &askpass_socket)?; let _task = executor.spawn(async move { maybe!(async move { let listener = @@ -334,23 +330,51 @@ pub fn set_askpass_program(path: std::path::PathBuf) { #[inline] #[cfg(not(target_os = "windows"))] -fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String { - format!( +fn generate_askpass_script( + shell_kind: ShellKind, + askpass_program: &std::path::Path, + askpass_socket: &std::path::Path, +) -> Result { + let askpass_program = shell_kind.prepend_command_prefix( + askpass_program + .to_str() + .context("Askpass program is on a non-utf8 path")?, + ); + let askpass_program = shell_kind + .try_quote_prefix_aware(&askpass_program) + .context("Failed to shell-escape Askpass program path")?; + let askpass_socket = askpass_socket + .try_shell_safe(shell_kind) + .context("Failed to shell-escape Askpass socket path")?; + let print_args = "printf '%s\\0' \"$@\""; + let shebang = "#!/bin/sh"; + Ok(format!( "{shebang}\n{print_args} | {askpass_program} --askpass={askpass_socket} 2> /dev/null \n", - askpass_socket = askpass_socket.display(), - print_args = "printf '%s\\0' \"$@\"", - shebang = "#!/bin/sh", - ) + )) } #[inline] #[cfg(target_os = "windows")] -fn generate_askpass_script(askpass_program: &str, askpass_socket: &std::path::Path) -> String { - format!( +fn generate_askpass_script( + shell_kind: ShellKind, + askpass_program: &std::path::Path, + askpass_socket: &std::path::Path, +) -> Result { + let askpass_program = shell_kind.prepend_command_prefix( + askpass_program + .to_str() + .context("Askpass program is on a non-utf8 path")?, + ); + let askpass_program = shell_kind + .try_quote_prefix_aware(&askpass_program) + .context("Failed to shell-escape Askpass program path")?; + let askpass_socket = askpass_socket + .try_shell_safe(shell_kind) + .context("Failed to shell-escape Askpass socket path")?; + Ok(format!( r#" $ErrorActionPreference = 'Stop'; ($args -join [char]0) | & {askpass_program} --askpass={askpass_socket} 2> $null "#, - askpass_socket = askpass_socket.display(), - ) + )) } diff --git a/crates/remote/src/transport/ssh.rs b/crates/remote/src/transport/ssh.rs index 049ec3575a4b99438587dab2d048503259eb1618..cf8e6f3e9cc9599aa7d2d05ea204c550892ac4c4 100644 --- a/crates/remote/src/transport/ssh.rs +++ b/crates/remote/src/transport/ssh.rs @@ -113,7 +113,7 @@ impl MasterProcess { .args(additional_args) .args(args); - master_process.arg(format!("ControlPath={}", socket_path.display())); + master_process.arg(format!("ControlPath='{}'", socket_path.display())); let process = master_process.arg(&url).spawn()?;