Harden Windows SSH config quoting
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s

This commit is contained in:
DuProcess
2026-07-16 20:07:34 -04:00
parent 77caebf310
commit 440e838bbd
+67 -8
View File
@@ -4711,13 +4711,46 @@ fn shell_word(value: &str) -> String {
}
fn ssh_config_word(value: &str) -> String {
if cfg!(windows) {
format!("\"{}\"", value.replace('"', "\\\""))
ssh_config_word_for_os(std::env::consts::OS, value)
}
fn ssh_config_word_for_os(os: &str, value: &str) -> String {
if os == "windows" {
windows_command_word(value)
} else {
shell_word(value)
}
}
fn windows_command_word(value: &str) -> String {
let mut out = String::from("\"");
let mut backslashes = 0usize;
for ch in value.chars() {
if ch == '\\' {
backslashes += 1;
continue;
}
if ch == '"' {
for _ in 0..(backslashes * 2 + 1) {
out.push('\\');
}
out.push('"');
backslashes = 0;
continue;
}
for _ in 0..backslashes {
out.push('\\');
}
backslashes = 0;
out.push(ch);
}
for _ in 0..(backslashes * 2) {
out.push('\\');
}
out.push('"');
out
}
fn local_bootstrap(
session: &str,
mode: &str,
@@ -10135,12 +10168,13 @@ mod tests {
should_hold_post_submit_input, should_reconnect_before_input_for_local_sleep,
should_repaint_idle_terminal, should_strip_unowned_terminal_reports,
split_after_command_submit, split_trace_tokens, ssh_command_target, ssh_config_uses_proxy,
ssh_destination_host, ssh_username, ssh_with_user, startup_command, status_ssh_target,
strip_stale_mouse_reports, strip_terminal_focus_reports, strip_unowned_terminal_reports,
summarize_trace_file, summarize_trace_file_with_mode, terminal_private_mode_transition,
toml_bare_key_or_quoted, top_trace_events, trace_report_warnings, unix_update_script,
update_binary_version_for_installer, update_installer_url, update_version_status,
upsert_managed_block, valid_forward_host, vscode_safe_alias, wake_repaint_retry_deadline,
ssh_config_word_for_os, ssh_destination_host, ssh_username, ssh_with_user, startup_command,
status_ssh_target, strip_stale_mouse_reports, strip_terminal_focus_reports,
strip_unowned_terminal_reports, summarize_trace_file, summarize_trace_file_with_mode,
terminal_private_mode_transition, toml_bare_key_or_quoted, top_trace_events,
trace_report_warnings, unix_update_script, update_binary_version_for_installer,
update_installer_url, update_version_status, upsert_managed_block, valid_forward_host,
vscode_safe_alias, wake_repaint_retry_deadline, windows_command_word,
windows_mode_from_readonly, windows_readonly_from_mode, windows_update_script,
};
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
@@ -10503,6 +10537,31 @@ mod tests {
);
}
#[test]
fn ssh_config_word_quotes_for_unix_and_windows() {
assert_eq!(
ssh_config_word_for_os("linux", "/Users/palav/My App/dosh"),
"'/Users/palav/My App/dosh'"
);
assert_eq!(
ssh_config_word_for_os("windows", r#"C:\Users\palav\My App\dosh.exe"#),
r#""C:\Users\palav\My App\dosh.exe""#
);
}
#[test]
fn windows_command_word_escapes_quotes_and_trailing_backslashes() {
assert_eq!(
windows_command_word(r#"C:\Program Files\dosh\"#),
r#""C:\Program Files\dosh\\""#
);
assert_eq!(windows_command_word(r#"say "hi""#), r#""say \"hi\"""#);
assert_eq!(
windows_command_word(r#"C:\path\before"quote"#),
r#""C:\path\before\"quote""#
);
}
#[test]
fn parses_ssh_config_native_parity_options() {
let parsed = parse_ssh_config(