fix(official-sync): 脱敏代理凭据避免泄漏到日志与状态输出

两处代理凭据泄漏:
- redact_proxy_url 对无 scheme 的代理串(user:secret@host:port,curl 默认按
  http 处理)完全不脱敏,明文凭据会进入 progress 事件、daemon 日志和
  bat-events.jsonl。现对无 scheme 但含 userinfo 的情况一并脱敏。
- daemon 保存的 command 含原始代理 URL,经 RPC status 和 bat status --json
  回显到 stdout(会被监控采集)。新增 redact_command_proxy_credentials,在
  build_daemon_status_report 的对外拷贝里脱敏 --proxy 值;磁盘状态文件
  (0600)仍保留原始 command 供 restart/reload 复用。

残留:子进程 argv 仍可经 /proc/<pid>/cmdline 读到代理凭据,以及 0600 状态
文件内的 at-rest 凭据,二者受进程属主与文件权限约束,后续可改由环境变量下
传子进程进一步收敛。

对应 issue #18 维护清单 1-2。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 04:31:10 -07:00
co-authored by Claude Fable 5
parent 7c2f6824a6
commit eef8c52497
2 changed files with 97 additions and 7 deletions
+29 -5
View File
@@ -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}://<redacted>@{}", &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}://<redacted>@{}", &rest[at_index + 1..])
format!("<redacted>@{}", &url[at_index + 1..])
}
fn proxy_env_pairs() -> Vec<(String, String)> {
@@ -576,4 +583,21 @@ mod tests {
"http://<redacted>@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"),
"<redacted>@127.0.0.1:7890"
);
assert_eq!(
redact_proxy_url("socks5://user:secret@127.0.0.1:1080"),
"socks5://<redacted>@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"
);
}
}