feat: mask all URLs embedded in an error/log string for safe logging

This commit is contained in:
wonfen 2026-02-22 07:37:40 +08:00
parent 0cc9bb2f30
commit 700011688b
No known key found for this signature in database
GPG Key ID: CEAFD6C73AB2001F
2 changed files with 42 additions and 4 deletions

View File

@ -2,7 +2,7 @@ use crate::{
cmd,
config::{Config, PrfItem, PrfOption, profiles::profiles_draft_update_item_safe},
core::{CoreManager, handle, tray},
utils::help::mask_url,
utils::help::{mask_err, mask_url},
};
use anyhow::{Result, bail};
use clash_verge_logging::{Type, logging, logging_error};
@ -129,7 +129,8 @@ async fn perform_profile_update(
logging!(
warn,
Type::Config,
"Warning: [订阅更新] 正常更新失败: {err}尝试使用Clash代理更新"
"Warning: [订阅更新] 正常更新失败: {}尝试使用Clash代理更新",
mask_err(&err.to_string())
);
last_err = err;
}
@ -150,7 +151,8 @@ async fn perform_profile_update(
logging!(
warn,
Type::Config,
"Warning: [订阅更新] 正常更新失败: {err}尝试使用Clash代理更新"
"Warning: [订阅更新] Clash代理更新失败: {},尝试使用系统代理更新",
mask_err(&err.to_string())
);
last_err = err;
}
@ -171,7 +173,8 @@ async fn perform_profile_update(
logging!(
warn,
Type::Config,
"Warning: [订阅更新] 正常更新失败: {err},尝试使用系统代理更新"
"Warning: [订阅更新] 系统代理更新失败: {},所有重试均已失败",
mask_err(&err.to_string())
);
last_err = err;
}

View File

@ -149,6 +149,41 @@ pub fn mask_url(url: &str) -> String {
result
}
/// Mask all URLs embedded in an error/log string for safe logging.
///
/// Scans the string for `http://` or `https://` and replaces each URL
/// (terminated by whitespace or `)`, `]`, `"`, `'`) with its masked form.
/// Text between URLs is copied verbatim.
pub fn mask_err(err: &str) -> String {
let mut result = String::with_capacity(err.len());
let mut remaining = err;
loop {
let http = remaining.find("http://");
let https = remaining.find("https://");
let start = match (http, https) {
(None, None) => {
result.push_str(remaining);
break;
}
(Some(a), None) | (None, Some(a)) => a,
(Some(a), Some(b)) => a.min(b),
};
result.push_str(&remaining[..start]);
remaining = &remaining[start..];
let url_end = remaining
.find(|c: char| c.is_whitespace() || matches!(c, ')' | ']' | '"' | '\''))
.unwrap_or(remaining.len());
result.push_str(&mask_url(&remaining[..url_end]));
remaining = &remaining[url_end..];
}
result
}
/// get the last part of the url, if not found, return empty string
pub fn get_last_part_and_decode(url: &str) -> Option<String> {
let path = url.split('?').next().unwrap_or(""); // Splits URL and takes the path part