From 4269f889bb57b3dd80fd3158c3fac7921592dd5e Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 25 Dec 2025 06:42:21 -0500 Subject: [PATCH] Avoid flagging proxied Git URLs as ambiguous authority (#17234) ## Summary Closes https://github.com/astral-sh/uv/issues/17214. --- crates/uv-redacted/src/lib.rs | 51 ++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/crates/uv-redacted/src/lib.rs b/crates/uv-redacted/src/lib.rs index 9a5adf280..7819d030c 100644 --- a/crates/uv-redacted/src/lib.rs +++ b/crates/uv-redacted/src/lib.rs @@ -58,6 +58,29 @@ pub enum DisplaySafeUrlError { #[repr(transparent)] pub struct DisplaySafeUrl(Url); +/// Check if a path or fragment contains a credential-like pattern (`:` followed by `@`). +/// +/// This skips colons that are followed by `//`, as those indicate URL schemes (e.g., `https://`) +/// rather than credentials. This is important for handling nested URLs like proxy URLs: +/// `git+https://proxy.com/https://github.com/user/repo.git@branch`. +fn has_credential_like_pattern(s: &str) -> bool { + let mut remaining = s; + while let Some(colon_pos) = remaining.find(':') { + let after_colon = &remaining[colon_pos + 1..]; + // If the colon is followed by "//", consider it a URL scheme. + if after_colon.starts_with("//") { + remaining = after_colon; + continue; + } + // Check if there's an @ after this colon. + if after_colon.contains('@') { + return true; + } + remaining = after_colon; + } + false +} + impl DisplaySafeUrl { #[inline] pub fn parse(input: &str) -> Result { @@ -93,17 +116,10 @@ impl DisplaySafeUrl { } // Check for the suspicious pattern. - if !url - .path() - .find(':') - .is_some_and(|pos| url.path()[pos..].contains('@')) + if !has_credential_like_pattern(url.path()) && !url .fragment() - .map(|fragment| { - fragment - .find(':') - .is_some_and(|pos| fragment[pos..].contains('@')) - }) + .map(has_credential_like_pattern) .unwrap_or(false) { return Ok(()); @@ -486,12 +502,27 @@ mod tests { #[test] fn parse_url_not_ambiguous() { - #[allow(clippy::single_element_loop)] for url in &[ // https://github.com/astral-sh/uv/issues/16756 "file:///C:/jenkins/ython_Environment_Manager_PR-251@2/venv%201/workspace", + // https://github.com/astral-sh/uv/issues/17214 + // Git proxy URLs with nested schemes should not trigger the ambiguity check + "git+https://githubproxy.cc/https://github.com/user/repo.git@branch", + "git+https://proxy.example.com/https://github.com/org/project@v1.0.0", + "git+https://proxy.example.com/https://github.com/org/project@refs/heads/main", ] { DisplaySafeUrl::parse(url).unwrap(); } } + + #[test] + fn credential_like_pattern() { + assert!(!has_credential_like_pattern( + "/https://github.com/user/repo.git@branch" + )); + assert!(!has_credential_like_pattern("/http://example.com/path@ref")); + + assert!(has_credential_like_pattern("/name:password@domain/a/b/c")); + assert!(has_credential_like_pattern(":password@domain")); + } }