优化: AI授权目录改进(误弹窗根因+安全加固)
strip_verbatim比对侧收口/黑名单统一/symlink/bind双路径reload/toast接线;cargo test 18✅
This commit is contained in:
@@ -130,14 +130,14 @@ fn validate_path(path: &str) -> anyhow::Result<()> {
|
||||
anyhow::bail!("禁止路径遍历 (..)");
|
||||
}
|
||||
|
||||
if lower.contains("\\.ssh")
|
||||
|| lower.contains("\\.aws")
|
||||
|| lower.contains("\\.gnupg")
|
||||
|| lower.contains("\\appdata\\")
|
||||
|| lower.contains("\\programdata\\")
|
||||
|| lower.contains("\\windows\\")
|
||||
|| lower.contains("\\system32\\")
|
||||
{
|
||||
// 敏感系统目录(.ssh/.aws/.gnupg + Windows/ProgramFiles/ProgramData + Unix 核心):
|
||||
// 统一走 state::is_in_system_blacklist 分段匹配(消除 contains 子串误伤如 my\windows\backup,
|
||||
// 与 is_authorized 运行时黑名单同源,单一来源 — 修 agent1 问题4 两套黑名单不一致)
|
||||
if crate::state::is_in_system_blacklist(&PathBuf::from(&normalized)) {
|
||||
anyhow::bail!("禁止访问敏感系统目录");
|
||||
}
|
||||
// AppData 保留单独 contains(用户级数据,分段匹配会误伤 D:\backup\appdata 这类合法目录名)
|
||||
if lower.contains("\\appdata\\") {
|
||||
anyhow::bail!("禁止访问敏感系统目录");
|
||||
}
|
||||
Ok(())
|
||||
@@ -542,6 +542,10 @@ fn register_project_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
|
||||
let stack_opt = args.get("stack").and_then(|v| v.as_str()).filter(|s| !s.is_empty());
|
||||
let repo = df_storage::crud::ProjectRepo::new(&db);
|
||||
let stack = bind_dir_to_project(&repo, id, path, stack_opt).await?;
|
||||
// F-260619-03: 项目绑定目录加 AI 白名单由 reload_allowed_dirs 自动读 projects.bind_directory
|
||||
// 统一处理(优雅:绑定 = 自动授权,reload 时合并)。bind handler 不直接写 allowed_dirs
|
||||
// (register_project_tools 无 allowed_dirs 参数,且 reload 集中更易维护)。
|
||||
// 绑定后触发 reload:通过 app_handle emit 事件让 state reload,或下次工具调用前 reload 兜底。
|
||||
Ok(serde_json::json!({ "id": id, "path": path, "stack": stack, "bound": true }))
|
||||
})
|
||||
})},
|
||||
@@ -1790,14 +1794,20 @@ fn search_files_recursive<'a>(
|
||||
.map_err(|e| anyhow::anyhow!("无法读取目录 {}: {}", path, e))?;
|
||||
while let Some(entry) = dir.next_entry().await? {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
let metadata = entry.metadata().await?;
|
||||
let is_dir = metadata.is_dir();
|
||||
// file_type 不跟随 symlink(对比 entry.metadata 会跟随);symlink 目录递归会逃逸出白名单
|
||||
// (workspace 内 symlink → 外部目录),故 symlink 一律跳过(不匹配/不递归)。对齐 list_dir_recursive。
|
||||
let file_type = entry.file_type().await?;
|
||||
if file_type.is_symlink() {
|
||||
continue;
|
||||
}
|
||||
let is_dir = file_type.is_dir();
|
||||
if !is_dir {
|
||||
// 字符串包含匹配(大小写不敏感)
|
||||
if name.to_lowercase().contains(pattern) {
|
||||
*total += 1;
|
||||
if results.len() < max_results {
|
||||
let full_path = std::path::Path::new(path).join(&name).to_string_lossy().into_owned();
|
||||
let metadata = entry.metadata().await?;
|
||||
results.push(serde_json::json!({ "path": full_path, "size": metadata.len() }));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user