mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 07:06:43 +08:00
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:
@@ -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<DaemonStatusRe
|
||||
next_forced_refresh_unix_seconds: status_file
|
||||
.as_ref()
|
||||
.and_then(|status| status.next_forced_refresh_unix_seconds),
|
||||
command: status_file.map(|status| status.command),
|
||||
command: status_file.map(|status| redact_command_proxy_credentials(status.command)),
|
||||
})
|
||||
}
|
||||
|
||||
/// Redacts proxy credentials in a saved daemon command before it is surfaced to
|
||||
/// callers (RPC `status`, `bat status --json`, human output).
|
||||
///
|
||||
/// The on-disk status file keeps the raw command because `restart`/`reload`
|
||||
/// relaunch the daemon from it; only the externally visible copy is redacted so
|
||||
/// credentials never reach stdout, monitoring, or log collection.
|
||||
fn redact_command_proxy_credentials(command: Vec<String>) -> Vec<String> {
|
||||
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://<redacted>@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], "<redacted>@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)
|
||||
|
||||
Reference in New Issue
Block a user