Display ssh git username in Debug implementation (#13806)

This updates the `Debug` implementation of `DisplaySafeUrl` to display
the git username in a case like `git+ssh://git@github.com/org/repo`. It
also factors out the git username check into a shared function and adds
related unit tests to `DisplaySafeUrl`.
This commit is contained in:
John Mumm 2025-06-03 10:52:51 -04:00 committed by GitHub
parent a96e766b55
commit 90aefe4a4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -91,10 +91,7 @@ impl DisplaySafeUrl {
pub fn remove_credentials(&mut self) {
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
// username.
if matches!(self.0.scheme(), "git+ssh" | "ssh")
&& self.0.username() == "git"
&& self.0.password().is_none()
{
if is_ssh_git_username(&self.0) {
return;
}
let _ = self.0.set_username("");
@ -131,7 +128,11 @@ impl Display for DisplaySafeUrl {
impl Debug for DisplaySafeUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let url = &self.0;
let (username, password) = if url.username() != "" && url.password().is_some() {
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid masking the
// username.
let (username, password) = if is_ssh_git_username(url) {
(url.username(), None)
} else if url.username() != "" && url.password().is_some() {
(url.username(), Some("****"))
} else if url.username() != "" {
("****", None)
@ -175,6 +176,10 @@ impl FromStr for DisplaySafeUrl {
}
}
fn is_ssh_git_username(url: &Url) -> bool {
matches!(url.scheme(), "ssh" | "git+ssh") && url.username() == "git" && url.password().is_none()
}
fn display_with_redacted_credentials(
url: &Url,
f: &mut std::fmt::Formatter<'_>,
@ -185,10 +190,7 @@ fn display_with_redacted_credentials(
// For URLs that use the `git` convention (i.e., `ssh://git@github.com/...`), avoid dropping the
// username.
if matches!(url.scheme(), "git+ssh" | "ssh")
&& url.username() == "git"
&& url.password().is_none()
{
if is_ssh_git_username(url) {
return write!(f, "{url}");
}
@ -231,7 +233,7 @@ mod tests {
let log_safe_url = DisplaySafeUrl::from(url);
assert_eq!(log_safe_url.username(), "");
assert!(log_safe_url.password().is_none());
assert_eq!(format!("{log_safe_url}"), url_str);
assert_eq!(log_safe_url.to_string(), url_str);
}
#[test]
@ -242,7 +244,7 @@ mod tests {
assert_eq!(log_safe_url.username(), "user");
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
);
}
@ -255,7 +257,7 @@ mod tests {
assert_eq!(log_safe_url.username(), "");
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://:****@pypi-proxy.fly.dev/basic-auth/simple"
);
}
@ -268,11 +270,26 @@ mod tests {
assert_eq!(log_safe_url.username(), "user");
assert!(log_safe_url.password().is_none());
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://****@pypi-proxy.fly.dev/basic-auth/simple"
);
}
#[test]
fn from_url_git_username() {
let ssh_str = "ssh://git@github.com/org/repo";
let ssh_url = DisplaySafeUrl::parse(ssh_str).unwrap();
assert_eq!(ssh_url.username(), "git");
assert!(ssh_url.password().is_none());
assert_eq!(ssh_url.to_string(), ssh_str);
// Test again for the `git+ssh` scheme
let git_ssh_str = "git+ssh://git@github.com/org/repo";
let git_ssh_url = DisplaySafeUrl::parse(git_ssh_str).unwrap();
assert_eq!(git_ssh_url.username(), "git");
assert!(git_ssh_url.password().is_none());
assert_eq!(git_ssh_url.to_string(), git_ssh_str);
}
#[test]
fn parse_url_string() {
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
@ -280,7 +297,7 @@ mod tests {
assert_eq!(log_safe_url.username(), "user");
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
);
}
@ -293,17 +310,34 @@ mod tests {
assert_eq!(log_safe_url.username(), "");
assert!(log_safe_url.password().is_none());
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://pypi-proxy.fly.dev/basic-auth/simple"
);
}
#[test]
fn preserve_ssh_git_username_on_remove_credentials() {
let ssh_str = "ssh://git@pypi-proxy.fly.dev/basic-auth/simple";
let mut ssh_url = DisplaySafeUrl::parse(ssh_str).unwrap();
ssh_url.remove_credentials();
assert_eq!(ssh_url.username(), "git");
assert!(ssh_url.password().is_none());
assert_eq!(ssh_url.to_string(), ssh_str);
// Test again for `git+ssh` scheme
let git_ssh_str = "git+ssh://git@pypi-proxy.fly.dev/basic-auth/simple";
let mut git_shh_url = DisplaySafeUrl::parse(git_ssh_str).unwrap();
git_shh_url.remove_credentials();
assert_eq!(git_shh_url.username(), "git");
assert!(git_shh_url.password().is_none());
assert_eq!(git_shh_url.to_string(), git_ssh_str);
}
#[test]
fn displayable_with_credentials() {
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
assert_eq!(
&log_safe_url.displayable_with_credentials().to_string(),
log_safe_url.displayable_with_credentials().to_string(),
url_str
);
}
@ -313,7 +347,7 @@ mod tests {
let url_str = "https://token@example.com/abc/";
let log_safe_url = DisplaySafeUrl::parse(url_str).unwrap();
let foo_url = log_safe_url.join("foo").unwrap();
assert_eq!(format!("{foo_url}"), "https://****@example.com/abc/foo");
assert_eq!(foo_url.to_string(), "https://****@example.com/abc/foo");
}
#[test]
@ -324,7 +358,7 @@ mod tests {
assert_eq!(log_safe_url.username(), "user");
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
assert_eq!(
format!("{log_safe_url}"),
log_safe_url.to_string(),
"https://user:****@pypi-proxy.fly.dev/basic-auth/simple"
);
}