修复: FR-S1 api_key IPC mask + 编辑不回填(防前端拿明文,P0 安全)
- ai_list_providers: 返回前 mask api_key(首尾各4+••••),IPC 不传明文给前端 realm - ai_save_provider: api_key 空(编辑不改)→保留原 DB 值 - Settings: 编辑不回填 apiKey(留空=不改) + saveProvider 校验编辑时 apiKey 可空 - 注:DB 仍明文存储(c,keyring 加密存储)留迁移单列;本轮解 IPC 传明文 + 前端 realm 持有明文 来源 fullstack-review §2 P0 安全;cargo/vue-tsc 0 err
This commit is contained in:
@@ -265,10 +265,28 @@ pub async fn ai_chat_stop(state: State<'_, AppState>, app: AppHandle) -> Result<
|
||||
// 提供商管理
|
||||
// ============================================================
|
||||
|
||||
/// api_key 脱敏:IPC 不传明文给前端(FR-S1),保留首尾各 4 字符便于辨识
|
||||
fn mask_api_key(key: &str) -> String {
|
||||
let chars: Vec<char> = key.chars().collect();
|
||||
if chars.len() <= 8 {
|
||||
return "•".repeat(chars.len());
|
||||
}
|
||||
let prefix: String = chars[..4].iter().collect();
|
||||
let suffix: String = chars[chars.len() - 4..].iter().collect();
|
||||
format!("{}••••{}", prefix, suffix)
|
||||
}
|
||||
|
||||
/// 列出所有已配置的 AI 提供商(is_default 真相源为 DB,重启不丢)
|
||||
#[tauri::command]
|
||||
pub async fn ai_list_providers(state: State<'_, AppState>) -> Result<Vec<AiProviderRecord>, String> {
|
||||
state.ai_providers.list_all().await.map_err(|e| e.to_string())
|
||||
let mut providers = state.ai_providers.list_all().await.map_err(|e| e.to_string())?;
|
||||
// IPC 不传明文 api_key(FR-S1):前端编辑用空 apiKey 表示不改,mask 后前端 realm 不持有明文
|
||||
for p in &mut providers {
|
||||
if !p.api_key.is_empty() {
|
||||
p.api_key = mask_api_key(&p.api_key);
|
||||
}
|
||||
}
|
||||
Ok(providers)
|
||||
}
|
||||
|
||||
/// 保存/更新 AI 提供商配置
|
||||
@@ -300,6 +318,18 @@ pub async fn ai_save_provider(
|
||||
.map_err(|e| e.to_string())?
|
||||
.iter().any(|p| p.is_default),
|
||||
};
|
||||
// api_key 空(编辑不改 key,前端 list 拿到 mask 故留空)→保留原 DB 值(FR-S1)
|
||||
let api_key = if api_key.is_empty() {
|
||||
match &id {
|
||||
Some(pid) => state.ai_providers.get_by_id(pid).await
|
||||
.map_err(|e| e.to_string())?
|
||||
.map(|p| p.api_key)
|
||||
.unwrap_or_default(),
|
||||
None => String::new(),
|
||||
}
|
||||
} else {
|
||||
api_key
|
||||
};
|
||||
let record = AiProviderRecord {
|
||||
id: id.unwrap_or_else(new_id),
|
||||
name,
|
||||
|
||||
Reference in New Issue
Block a user