mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-07-22 01:46:44 +08:00
fix(official-sync): curl 代理改由环境变量传入,堵住 argv 泄漏
apply_curl_proxy 原来对 URL 型代理执行 command.arg("--proxy").arg(url),
把可能含凭据的代理 URL 放进 curl 子进程 argv,会出现在 curl 进程的
/proc/<pid>/cmdline(0444,世界可读,每次下载存活期间可被本地任意用户读到)。
改为先 remove_proxy_env 清除所有继承的代理变量,再通过 ALL_PROXY/all_proxy
环境变量把代理传给 curl(大小写两种写法都设,避免 curl 只读其一)。凭据从
世界可读的 cmdline 转移到仅属主可读的 /proc/<pid>/environ(0400)。no_proxy
列表不含凭据,仍以 --noproxy 参数传入;disabled 模式保持 --noproxy * 直连。
至此代理凭据泄漏面全部堵住:daemon 子进程 argv、0600 状态文件(55b9bab),
以及本次的 curl 孙进程 argv。
验证:
- 新增单测用 Command::get_args/get_envs 断言 URL 只进 ALL_PROXY 环境变量、
不进 argv,且 scheme 专用变量被清除;disabled 模式强制直连。
- 真机经本地混合代理 127.0.0.1:7897 端到端验证:curl shim 记录 11 次调用,
凭据 0 次进 argv、0 个 --proxy 参数,代理仅在 ALL_PROXY 环境变量中;curl
确实经该代理到达官方 launcher/CDN/resources.assets 端点,功能未回归。
- 对抗性审查确认 auto/disabled/no_proxy 语义与旧 --proxy 等价,env 先删后设
最终为 set,无其它遗漏的凭据落 argv/日志/状态文件路径。
对应 issue #18 维护清单 1-2 残留项(curl 子进程)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -423,8 +423,13 @@ fn apply_curl_proxy(command: &mut Command, proxy: &ResolvedCurlProxy) {
|
||||
}
|
||||
|
||||
if let Some(url) = proxy.url.as_ref() {
|
||||
// 通过环境变量而非 --proxy 参数把代理传给 curl:代理 URL 可能带凭据,
|
||||
// 放进 argv 会出现在 curl 子进程的 /proc/<pid>/cmdline(0444,世界可读)。
|
||||
// 先清除所有继承的代理变量,再设 ALL_PROXY/all_proxy(覆盖 http 与 https,
|
||||
// 大小写两种写法都设,避免 curl 只读其中一种)。no_proxy 列表不含凭据,仍走参数。
|
||||
remove_proxy_env(command);
|
||||
command.arg("--proxy").arg(url);
|
||||
command.env("ALL_PROXY", url);
|
||||
command.env("all_proxy", url);
|
||||
if let Some(no_proxy) = proxy.no_proxy.as_ref() {
|
||||
command.arg("--noproxy").arg(no_proxy);
|
||||
}
|
||||
@@ -600,4 +605,72 @@ mod tests {
|
||||
"http://127.0.0.1:7890"
|
||||
);
|
||||
}
|
||||
|
||||
fn command_args(command: &Command) -> Vec<String> {
|
||||
command
|
||||
.get_args()
|
||||
.map(|arg| arg.to_string_lossy().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn command_env(command: &Command, key: &str) -> Option<Option<String>> {
|
||||
command
|
||||
.get_envs()
|
||||
.find(|(name, _)| *name == key)
|
||||
.map(|(_, value)| value.map(|value| value.to_string_lossy().to_string()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_curl_proxy_passes_url_via_env_not_argv() {
|
||||
let proxy = ResolvedCurlProxy {
|
||||
enabled: true,
|
||||
disabled: false,
|
||||
url: Some("http://user:secret@127.0.0.1:7897".to_string()),
|
||||
source: "--proxy".to_string(),
|
||||
no_proxy: Some("localhost,127.0.0.1".to_string()),
|
||||
};
|
||||
let mut command = Command::new("curl");
|
||||
apply_curl_proxy(&mut command, &proxy);
|
||||
|
||||
// 凭据不得出现在 curl 的 argv。
|
||||
let args = command_args(&command);
|
||||
assert!(!args.iter().any(|arg| arg.contains("secret")));
|
||||
assert!(!args.iter().any(|arg| arg == "--proxy"));
|
||||
// no_proxy 列表不含凭据,仍以参数传入。
|
||||
assert!(args
|
||||
.windows(2)
|
||||
.any(|pair| pair == ["--noproxy", "localhost,127.0.0.1"]));
|
||||
|
||||
// 代理 URL 经 ALL_PROXY/all_proxy 环境变量下传。
|
||||
assert_eq!(
|
||||
command_env(&command, "ALL_PROXY"),
|
||||
Some(Some("http://user:secret@127.0.0.1:7897".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
command_env(&command, "all_proxy"),
|
||||
Some(Some("http://user:secret@127.0.0.1:7897".to_string()))
|
||||
);
|
||||
// 继承的 scheme 专用代理变量被清除,避免覆盖 ALL_PROXY。
|
||||
assert_eq!(command_env(&command, "HTTPS_PROXY"), Some(None));
|
||||
assert_eq!(command_env(&command, "https_proxy"), Some(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_curl_proxy_disabled_forces_direct() {
|
||||
let proxy = ResolvedCurlProxy {
|
||||
enabled: false,
|
||||
disabled: true,
|
||||
url: None,
|
||||
source: "--no-proxy".to_string(),
|
||||
no_proxy: None,
|
||||
};
|
||||
let mut command = Command::new("curl");
|
||||
apply_curl_proxy(&mut command, &proxy);
|
||||
|
||||
let args = command_args(&command);
|
||||
assert!(args.windows(2).any(|pair| pair == ["--noproxy", "*"]));
|
||||
// 直连模式清除所有继承的代理变量,且不设置任何代理。
|
||||
assert_eq!(command_env(&command, "ALL_PROXY"), Some(None));
|
||||
assert_eq!(command_env(&command, "HTTPS_PROXY"), Some(None));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user