put: expand ~ in ssh_config IdentityFile paths
IdentityFile values were stored verbatim, so the near-universal 'IdentityFile ~/.ssh/key' spelling never matched an existing file and the key was silently skipped during authentication. Expand a leading ~ (only that form; ~user and embedded tildes stay verbatim) against the user's home directory when parsing.
This commit is contained in:
+62
-5
@@ -120,7 +120,7 @@ fn apply_config_file(config: &mut SshConfig, content: &str, host: &str) {
|
||||
config.port = Some(port);
|
||||
}
|
||||
} else if keyword.eq_ignore_ascii_case("identityfile") {
|
||||
let path = PathBuf::from(rest);
|
||||
let path = expand_tilde(rest);
|
||||
if !config.identity_files.contains(&path) {
|
||||
config.identity_files.push(path);
|
||||
}
|
||||
@@ -128,6 +128,37 @@ fn apply_config_file(config: &mut SshConfig, content: &str, host: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The current user's home directory: the `directories` crate first (which
|
||||
/// consults `$HOME` on Unix), with a direct `$HOME` fallback.
|
||||
fn current_home() -> Option<PathBuf> {
|
||||
directories::UserDirs::new()
|
||||
.map(|dirs| dirs.home_dir().to_path_buf())
|
||||
.or_else(|| std::env::var_os("HOME").map(PathBuf::from))
|
||||
}
|
||||
|
||||
/// Expand a leading `~` or `~/` in `path` against `home`: the near-universal
|
||||
/// `IdentityFile ~/.ssh/key` spelling. Only a leading tilde is handled —
|
||||
/// `~user/...` (another user's home) and tildes appearing anywhere else are
|
||||
/// kept verbatim — and without a known home directory the path is returned
|
||||
/// unchanged.
|
||||
fn expand_tilde_with(path: &str, home: Option<&Path>) -> PathBuf {
|
||||
let Some(home) = home else {
|
||||
return PathBuf::from(path);
|
||||
};
|
||||
if path == "~" {
|
||||
home.to_path_buf()
|
||||
} else if let Some(rest) = path.strip_prefix("~/") {
|
||||
home.join(rest)
|
||||
} else {
|
||||
PathBuf::from(path)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`expand_tilde_with`] against the current user's home directory
|
||||
fn expand_tilde(path: &str) -> PathBuf {
|
||||
expand_tilde_with(path, current_home().as_deref())
|
||||
}
|
||||
|
||||
/// One `Host` block pattern against a host name: exact match, `*` (any
|
||||
/// run), `?` (one character) and `!pattern` negation (a matching negated
|
||||
/// pattern excludes the host from the block).
|
||||
@@ -557,17 +588,43 @@ Host *
|
||||
);
|
||||
|
||||
assert_eq!(config.user.as_deref(), Some("myuser"));
|
||||
// A leading tilde is expanded to the home directory, built from the
|
||||
// same lookup the helper uses so no specific username is assumed
|
||||
let home = current_home().expect("tests require a home directory");
|
||||
assert_eq!(
|
||||
config.identity_files,
|
||||
vec![
|
||||
PathBuf::from("~/.ssh/lp_key"),
|
||||
PathBuf::from("~/.ssh/other_key")
|
||||
]
|
||||
vec![home.join(".ssh/lp_key"), home.join(".ssh/other_key")]
|
||||
);
|
||||
assert_eq!(config.host_name, None);
|
||||
assert_eq!(config.port, None);
|
||||
}
|
||||
|
||||
/// Only a leading `~`/`~/` expands to the home directory; `~user/...`
|
||||
/// and non-tilde paths are kept verbatim, and nothing is expanded
|
||||
/// without a known home directory
|
||||
#[test]
|
||||
fn expand_tilde_handles_leading_tilde_only() {
|
||||
let home = Some(Path::new("/home/testuser"));
|
||||
assert_eq!(
|
||||
expand_tilde_with("~/x", home),
|
||||
PathBuf::from("/home/testuser/x")
|
||||
);
|
||||
assert_eq!(
|
||||
expand_tilde_with("~", home),
|
||||
PathBuf::from("/home/testuser")
|
||||
);
|
||||
assert_eq!(
|
||||
expand_tilde_with("~other/x", home),
|
||||
PathBuf::from("~other/x")
|
||||
);
|
||||
assert_eq!(
|
||||
expand_tilde_with("relative", home),
|
||||
PathBuf::from("relative")
|
||||
);
|
||||
assert_eq!(expand_tilde_with("/abs", home), PathBuf::from("/abs"));
|
||||
assert_eq!(expand_tilde_with("~/x", None), PathBuf::from("~/x"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_wildcard_and_question_marks_match() {
|
||||
let config = apply_config_file_all(
|
||||
|
||||
Reference in New Issue
Block a user