diff --git a/infrastructure/src/bin/bat_official_sync.rs b/infrastructure/src/bin/bat_official_sync.rs index 4b07af0..825fe8e 100644 --- a/infrastructure/src/bin/bat_official_sync.rs +++ b/infrastructure/src/bin/bat_official_sync.rs @@ -1,6 +1,6 @@ use bat_adapters::official::yostar_jp::PatchPlatform; use bat_infrastructure::{ - lexical_absolute, open_append_file, read_file_no_symlink, read_version_state, + lexical_absolute, open_append_file, read_file_no_symlink, read_version_state, redact_proxy_url, resolve_curl_proxy, validate_output_root, validate_runtime_state_dir, write_file_atomic, CurlProxyConfig, CurlProxyMode, OfficialFailedVersionRecord, OfficialServerInfoSource, OfficialUpdateConfig, OfficialUpdateProgress, OfficialUpdateReport, OfficialUpdateService, @@ -1668,10 +1668,28 @@ fn build_daemon_status_report(state_dir: &Path) -> anyhow::Result) -> Vec { + let mut redacted = command; + for index in 0..redacted.len() { + if redacted[index] == "--proxy" { + if let Some(value) = redacted.get_mut(index + 1) { + *value = redact_proxy_url(value); + } + } + } + redacted +} + #[derive(Debug, Serialize)] struct DaemonControlReport { command: &'static str, @@ -5044,6 +5062,54 @@ mod tests { assert_eq!(OfficialUpdateStatus::Downloaded.as_str(), "downloaded"); } + #[test] + fn redacts_proxy_credentials_in_surfaced_command() { + let command = vec![ + "/opt/bat".to_string(), + "--auto-discover".to_string(), + "--proxy".to_string(), + "http://user:secret@127.0.0.1:7890".to_string(), + "--output".to_string(), + "/var/lib/bat".to_string(), + ]; + + let redacted = redact_command_proxy_credentials(command); + + assert_eq!(redacted[3], "http://@127.0.0.1:7890"); + assert!(!redacted.join(" ").contains("secret")); + // 非代理参数保持原样。 + assert_eq!(redacted[1], "--auto-discover"); + assert_eq!(redacted[5], "/var/lib/bat"); + } + + #[test] + fn redacts_schemeless_proxy_credentials_in_surfaced_command() { + let command = vec![ + "/opt/bat".to_string(), + "--proxy".to_string(), + "user:secret@127.0.0.1:7890".to_string(), + ]; + + let redacted = redact_command_proxy_credentials(command); + + assert_eq!(redacted[2], "@127.0.0.1:7890"); + assert!(!redacted.join(" ").contains("secret")); + } + + #[test] + fn leaves_command_without_proxy_untouched() { + let command = vec![ + "/opt/bat".to_string(), + "--auto-discover".to_string(), + "--proxy".to_string(), + "auto".to_string(), + ]; + + let redacted = redact_command_proxy_credentials(command.clone()); + + assert_eq!(redacted, command); + } + fn beijing_time(day: u64, hour: u64, minute: u64, second: u64) -> SystemTime { system_time_from_beijing_local_seconds( day.saturating_mul(SECONDS_PER_DAY) diff --git a/infrastructure/src/curl_transfer.rs b/infrastructure/src/curl_transfer.rs index fa133c7..c462737 100644 --- a/infrastructure/src/curl_transfer.rs +++ b/infrastructure/src/curl_transfer.rs @@ -108,14 +108,21 @@ pub fn resolve_curl_proxy(config: &CurlProxyConfig) -> ResolvedCurlProxy { } /// Redacts user info from a proxy URL for diagnostics and logs. +/// +/// `curl` accepts proxy strings without an explicit scheme (defaulting to +/// `http://`), so credentials such as `user:secret@host:port` must be redacted +/// even when no `://` separator is present. pub fn redact_proxy_url(url: &str) -> String { - let Some((scheme, rest)) = url.split_once("://") else { + if let Some((scheme, rest)) = url.split_once("://") { + let Some(at_index) = rest.find('@') else { + return url.to_string(); + }; + return format!("{scheme}://@{}", &rest[at_index + 1..]); + } + let Some(at_index) = url.find('@') else { return url.to_string(); }; - let Some(at_index) = rest.find('@') else { - return url.to_string(); - }; - format!("{scheme}://@{}", &rest[at_index + 1..]) + format!("@{}", &url[at_index + 1..]) } fn proxy_env_pairs() -> Vec<(String, String)> { @@ -576,4 +583,21 @@ mod tests { "http://@127.0.0.1:7890" ); } + + #[test] + fn redact_proxy_url_handles_schemeless_credentials() { + assert_eq!( + redact_proxy_url("user:secret@127.0.0.1:7890"), + "@127.0.0.1:7890" + ); + assert_eq!( + redact_proxy_url("socks5://user:secret@127.0.0.1:1080"), + "socks5://@127.0.0.1:1080" + ); + assert_eq!(redact_proxy_url("127.0.0.1:7890"), "127.0.0.1:7890"); + assert_eq!( + redact_proxy_url("http://127.0.0.1:7890"), + "http://127.0.0.1:7890" + ); + } }