修复: 安全 8 项 + create_project 自动建目录

- ① relay token 必设环境变量 DF_RELAY_TOKEN,移除硬编码默认
- ② AiProviderRecord Debug 脱敏 api_key/model_configs/config
- ③ ScriptNode 危险命令告警(rm -rf/DROP TABLE/Format 等)
- ④ bind_directory 路径规范化,拒绝含 .. 的原始路径
- ⑤ StateMachine 文档警告不得在 await 期间持锁
- ⑥ EventBus send 错误改 tracing::warn 不再静默吞噬
- ⑦ probe_pwsh OnceLock 全局缓存,异步探测不阻塞 tokio
- ⑧ retry jitter 改 rand crate,范围扩大到 ±50%
- create_project 目录不存在时自动创建,消除死锁链
This commit is contained in:
2026-06-28 13:36:18 +08:00
parent f776336eb1
commit db46e74d3e
11 changed files with 110 additions and 34 deletions

View File

@@ -324,6 +324,10 @@ fn bind_directory(ctx: &Ctx, args: Value) -> BoxFuture<'static, CallToolResult>
medium_audit("bind_directory", &format!("{id} <- {path}"));
Box::pin(async move {
let repo = ProjectRepo::new(&db);
// 拒绝原始路径含 `..`(防穿越)
if path.contains("..") {
return CallToolResult::error(format!("路径不得包含 '..': {}", path));
}
let norm = normalize_path(&path);
// 路径冲突检测
if let Some(conflict) = repo.find_path_conflict(&norm, Some(&id)).await.ok().flatten() {
@@ -332,8 +336,8 @@ fn bind_directory(ctx: &Ctx, args: Value) -> BoxFuture<'static, CallToolResult>
conflict.name, conflict.id
));
}
// 仅更新 path 字段(用 update_field,保留其它)
if !repo.update_field(&id, "path", &path).await.unwrap_or(false) {
// 仅更新 path 字段(用 normalize 后的规范化路径,保留其它)
if !repo.update_field(&id, "path", &norm).await.unwrap_or(false) {
return CallToolResult::error(format!("项目不存在: {id}"));
}
let updated = repo.get_by_id(&id).await.ok().flatten();