From 8644c225549c5ef14166911b128e8b01874cd50f Mon Sep 17 00:00:00 2001 From: DuProcess <273172371+DuProcess@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:43:34 -0400 Subject: [PATCH] Use Windows username in client auth paths --- src/bin/dosh-client.rs | 99 ++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/bin/dosh-client.rs b/src/bin/dosh-client.rs index d33a873..a823338 100644 --- a/src/bin/dosh-client.rs +++ b/src/bin/dosh-client.rs @@ -4953,7 +4953,7 @@ fn local_bootstrap( let mut server_config = load_server_config(None)?; server_config.port = port; let secret = load_or_create_server_secret(&server_config)?; - let user = std::env::var("USER").unwrap_or_else(|_| "unknown".to_string()); + let user = local_username(); let nonce = crypto::random_12(); build_bootstrap( &server_config, @@ -5393,7 +5393,7 @@ async fn try_native_auth( }; let requested_user = ssh_username(server) .or(ssh_config.user.clone()) - .unwrap_or_else(|| std::env::var("USER").unwrap_or_else(|_| "unknown".to_string())); + .unwrap_or_else(local_username); let (client_secret, client_public) = generate_native_ephemeral(); let hello = dosh::native::NativeClientHello { protocol_version: dosh::native::NATIVE_PROTOCOL_VERSION, @@ -5544,7 +5544,7 @@ async fn try_native_auth_check( }; let requested_user = ssh_username(server) .or(ssh_config.user.clone()) - .unwrap_or_else(|| std::env::var("USER").unwrap_or_else(|_| "unknown".to_string())); + .unwrap_or_else(local_username); let (client_secret, client_public) = generate_native_ephemeral(); let hello = dosh::native::NativeClientHello { protocol_version: dosh::native::NATIVE_PROTOCOL_VERSION, @@ -5835,9 +5835,15 @@ fn ssh_path_token_context( } fn local_username() -> String { - std::env::var("USER") - .or_else(|_| std::env::var("USERNAME")) - .unwrap_or_else(|_| "unknown".to_string()) + local_username_from_env(|name| std::env::var(name).ok()) +} + +fn local_username_from_env(mut get: impl FnMut(&str) -> Option) -> String { + ["USER", "USERNAME"] + .into_iter() + .filter_map(|name| get(name)) + .find(|value| !value.is_empty()) + .unwrap_or_else(|| "unknown".to_string()) } fn expand_ssh_path_tokens(path: &str, context: &SshPathTokenContext) -> String { @@ -10401,33 +10407,33 @@ mod tests { expand_ssh_path_tokens, input_contains_focus_in, input_matches_escape, is_local_status_target, is_resume_response_for_client, latest_release_download_url, load_first_native_identity_with_prompt, local_symlink_target_is_dir, - native_proxy_udp_warning, newest_client_trace_path_from, parse_dynamic_forward, - parse_escape_key, parse_local_forward, parse_remote_forward, parse_single_remote_path, - parse_ssh_config, parse_trace_line, parse_trace_options, parse_trace_report_options, - parse_trace_summary, parse_update_options, post_submit_hold_duration, - queue_or_send_stream_data, queue_pending_user_input, queue_stale_pending_user_input, - raw_contains_host_table, recv_response_until, refresh_live_addr, release_artifact_name_for, - release_tag_download_url, release_tag_from_effective_url, release_version_from_tag, - remote_unix_server_update_script, render_frame_bytes, render_status_clear, - render_status_overlay, requested_env, resolve_forward_agent_endpoint, - resolved_startup_command, retire_stream_state, retransmit_stream_closes, - retransmit_stream_eofs, retransmit_stream_opens, retransmit_stream_window_adjusts, - rewrite_forward_command, sanitize_trace_name, selected_predict_mode, selected_udp_host, - send_stream_eof, server_version_mismatch, should_flush_terminal_input_after_contact, - should_health_log_client_start, should_hold_during_startup_gate, - 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_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_command_candidates, vscode_safe_alias, wake_repaint_retry_deadline, - windows_command_word, windows_deferred_update_script, windows_effective_url_script, - windows_mode_from_readonly, windows_readonly_from_mode, windows_update_script, - windows_url_reachable_script, windows_vt_output_mode, + local_username_from_env, native_proxy_udp_warning, newest_client_trace_path_from, + parse_dynamic_forward, parse_escape_key, parse_local_forward, parse_remote_forward, + parse_single_remote_path, parse_ssh_config, parse_trace_line, parse_trace_options, + parse_trace_report_options, parse_trace_summary, parse_update_options, + post_submit_hold_duration, queue_or_send_stream_data, queue_pending_user_input, + queue_stale_pending_user_input, raw_contains_host_table, recv_response_until, + refresh_live_addr, release_artifact_name_for, release_tag_download_url, + release_tag_from_effective_url, release_version_from_tag, remote_unix_server_update_script, + render_frame_bytes, render_status_clear, render_status_overlay, requested_env, + resolve_forward_agent_endpoint, resolved_startup_command, retire_stream_state, + retransmit_stream_closes, retransmit_stream_eofs, retransmit_stream_opens, + retransmit_stream_window_adjusts, rewrite_forward_command, sanitize_trace_name, + selected_predict_mode, selected_udp_host, send_stream_eof, server_version_mismatch, + should_flush_terminal_input_after_contact, should_health_log_client_start, + should_hold_during_startup_gate, 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_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_command_candidates, vscode_safe_alias, + wake_repaint_retry_deadline, windows_command_word, windows_deferred_update_script, + windows_effective_url_script, windows_mode_from_readonly, windows_readonly_from_mode, + windows_update_script, windows_url_reachable_script, windows_vt_output_mode, }; use dosh::config::{ClientConfig, CommandExtension, HostConfig}; use dosh::native::EnvVar; @@ -10982,6 +10988,33 @@ mod tests { ); } + #[test] + fn local_username_uses_unix_or_windows_environment_names() { + assert_eq!( + local_username_from_env(|name| match name { + "USER" => Some("palav".to_string()), + _ => None, + }), + "palav" + ); + assert_eq!( + local_username_from_env(|name| match name { + "USERNAME" => Some("palav-win".to_string()), + _ => None, + }), + "palav-win" + ); + assert_eq!( + local_username_from_env(|name| match name { + "USER" => Some(String::new()), + "USERNAME" => Some("palav-win".to_string()), + _ => None, + }), + "palav-win" + ); + assert_eq!(local_username_from_env(|_| None), "unknown"); + } + #[test] fn native_proxy_udp_warning_requires_missing_explicit_dosh_host() { let proxied = SshConfig {