Files
DevFlow/docs/05-代码审查/全量走查报告-2026-06-28.md
绝尘 fee856129c 新增: 全量走查报告 — 代码/前端/文档体系
- Rust 后端 12 crate: 锁/LIKE 通配符/jitter/路径穿越 等 15 项
- Vue 3 前端: Arco 残留/confirm 重复/类型退化 等 13 项
- 文档体系: 工具清单过时/INDEX 漏列 9 文档/死链/SSOT 违反 等 9 项
2026-06-28 04:53:33 +08:00

17 KiB
Raw Blame History

全量走查报告2026-06-28

范围:全栈 Rust 12 crate + Tauri 命令层 + Vue 3 前端 + 文档体系 方法3 路并行Rust 后端/Vue 前端/文档体系),主代理综合整理 原则dry — 仅审查 + 文档,不改代码


🔴 安全

① relay.rs — 硬编码默认 Token

文件: crates/df-relay/src/relay.rs:36-37

中继服务使用硬编码 DEFAULT_TOKEN = "devflow-relay-default-token",所有未设环境变量的部署均使用同一静态 token。通过 strings 即可从二进制提取。

建议:启动时要求必须设置 DF_RELAY_TOKEN 环境变量,移除默认值。

② models.rs — AiProviderRecord Debug 未脱敏 api_key日志泄漏风险

文件: crates/df-storage/src/models.rs:288

AiProviderRecord 未实现自定义 Debugapi_key 脱敏。若上层代码意外 {:?} 打印(如 tracing::debug!),明文 API key 会写入日志。model_configsL296configL298等 JSON 字段同样不安全。

建议:为 AiProviderRecord 实现自定义 Debug,将 api_key 输出为 "sk-****"

③ ScriptNode — 命令无白名单/沙箱

文件: crates/df-nodes/src/script_node.rs:15-19 + crates/df-execute/src/shell.rs:92-117

ScriptNode 从 config 取 command 字符串透传给 shellcmd /C / sh -c),无任何命令白名单或参数校验。工作流定义来自 AI 生成或外部导入时,恶意命令可通过 AINode 写入 ScriptNode config 注入执行。

建议:添加危险命令关键词告警(rm -rf/DROP TABLE/Format),可选白名单/环境变量隔离配置。

④ bind_directory — 原始路径入库未规范化 + 无沙箱校验

文件: crates/df-mcp/src/tools.rs:314-342

bind_directory 接收用户传入 pathnormalize_path 仅用于去重检测,原始 path 值直接存储。同一目录可用两种写法绑定两次绕过冲突检测。且未校验规范化后路径在项目沙箱允许范围内。

建议:存储前 normalize_path() + 校验在沙箱目录内 + 拒绝含 .. 的原始路径。

⑤ state.rs — std::sync::Mutex 在 async 上下文中持锁风险

文件: crates/df-workflow/src/state.rs:34-36, 55, 97, 104

StateMachine 使用 std::sync::Mutex 保护 HashMapget() 等同步方法中直接 lock().expect()DagExecutorfutures::future::join_all 同层并发时,若某节点持有引用后 .await 另一操作间接尝试锁 states,会死锁。通篇 4 处 expect——某节点 panic 导致锁中毒,连锁 fatal。

建议:改用 tokio::sync::Mutex或在文档中明确标注「state_machine 方法不得在 .await 期间持锁」。

⑥ eventbus.rs — 错误静默吞噬

文件: crates/df-workflow/src/eventbus.rs:16, 32

DEFAULT_CAPACITY: usize = 256EventBus::send 调用处 let _ 吞噬所有错误。当并发节点多且事件消费慢时256 溢出导致最早接收者 Lagged 错误。

建议sendtracing::warn! 记录 Lagged 错误。

⑦ shell.rs — probe_pwsh 同步阻塞 tokio 运行时

文件: crates/df-execute/src/shell.rs:53-66

probe_pwsh()Default::default() 中调用,Defaultexecute() 异步函数内调用。在 tokio 异步函数中同步阻塞 cmd.status() 违反最佳实践。

建议:惰性异步探测(OnceLocktokio::task::spawn_blocking)。

⑧ retry.rs — backoff jitter 随机性弱

文件: crates/df-ai/src/retry.rs:77-81

jitter 使用 SystemTime::now().duration_since().subsec_nanos() % 2000 映射到 [-1.0, 1.0)。纳秒级退化到 1ms 粒度时多个请求可能拿到同一值,重试碰撞风险。

建议:改用 fastrandjitter 范围扩大到 ±50%(当前 ±20%)。


🔴 文档

⑨ AI对话引擎-2026-06-14.md — 工具清单严重过时

文件: docs/03-模块文档/AI对话引擎-2026-06-14.md §四

标题自称"12 个"工具,表格实际列出 13 行(自相矛盾);与 df-ai-AI集成模块-2026-06-12.md 声称的 22 个工具严重对不齐;缺失 run_command/patch_file/list_trash/file_info/search_files/delete_task/restore_project/purge_project 等工具。

建议:全文同步至当前实际 22 个工具的最新清单。

⑩ 02-架构设计/INDEX.md — 漏索引 9 个已有文档

文件: docs/02-架构设计/INDEX.md

子目录 漏列数 典型遗漏
专项设计 7 AI原生上下文地图AST符号解析插件机制查询能力补全方案项目知识图谱
已编号方案 2 F-260622-01-跨端AIChat-Phase3联调设计消息级溯源P2-切读方案(今天新增)
构想审查 1 跑题改进试验记录
根目录 2 单对话并行多轮-Phase0落地路线图单对话并行多轮-设计

建议:批量补齐,建立「新增文档 → 立即更新 INDEX」的提交习惯。

⑪ Agent架构说明 — 死链

文件: docs/02-架构设计/专项设计/Agent架构说明-2026-06-14.md:4

> 关联: [任务推进设计](任务推进构想-2026-06-14.md)

目标文件在 构想审查/ 下,当前路径解析到 专项设计/ 下不存在。

建议:改为 [任务推进设计](../构想审查/任务推进构想-2026-06-14.md)

⑫ ARCHITECTURE.md §七 + 新设计文档 — 大段逐字重复

文件: ARCHITECTURE.md:538-665 / docs/02-架构设计/专项设计/三层模型-流程模板与人设体系-2026-06-28.md

YAML 模板示例52行AgentPersona struct 定义11行、三层定义表、实例化流程——大段逐字重复,违反文档记录规范 SSOT 原则。

建议ARCHITECTURE.md §七 保留概要(三层定义 + 关系图 + 关键原则YAML 全文和 struct 定义仅在专项设计文档展开ARCH 以引用链接替代。

⑬ ARCHITECTURE.md 仍保留 ~100 行已移除模块细节

文件: ARCHITECTURE.md:210-318df-task / df-traceability 章节)

保留 Task 生命周期图、分支策略、标注系统、需求-功能-测试映射、Decision struct 等已移除模块的详细设计。造成认知负担。

建议:将已移除模块的详细历史移至 功能决策记录-归档ARCHITECTURE.md 仅保留「已移除,见归档」。

⑭ 模块文档更新停滞(最长 16 天)

文档 最后更新 距今天数
df-workflow-工作流引擎-2026-06-12.md 06-15 13 天
df-ai-AI集成模块-2026-06-12.md 06-14 14 天
df-storage-存储层-2026-06-12.md 06-15 13 天
想法探索-对抗式评估-2026-06-12.md 无更新 16 天

建议:对上述文档做一次「代码对齐」检查。

⑮ ARCHITECTURE.md 版本状态过时

文件: ARCHITECTURE.md:3

当前 状态: 设计阶段。工程处于 Phase 2 验证阶段df-workflow 核心完成、AI ReAct 循环运行中、审批机制就绪),状态标签与实际不符。

建议:改为 状态: Phase 2 本地优先开发流程验证(与 docs/INDEX.md 一致)。

⑯ 新设计文档与现有文档缺失交叉引用

文件: docs/02-架构设计/专项设计/三层模型-流程模板与人设体系-2026-06-28.md

应当引用但未引用:全局事件数据总线df-nodes-节点集合任务推进构想

建议:补充上述交叉引用。

⑰ AI对话引擎文档边界归属模糊

文件: docs/03-模块文档/AI对话引擎-2026-06-14.md

位于 03-模块文档/,但内容大量描述 src-tauri/commands/ai/ 下的 Tauri 层代码,而非 crates/df-ai crate 内部。按文档记录规范职责矩阵,03-模块文档/ 应记录各 crate 实现细节。

建议:移入 02-架构设计/ 适当子目录,或明确标注为跨层交互设计文档。


🟡 代码质量

⑱ dag.rs — deep_merge 收到 null 返回 null覆盖全局配置

文件: crates/df-workflow/src/dag.rs:174-192

deep_merge(global, null) 返回 null。若某节点定义写了 "config": nullJSON 显式 null该节点 ctx.config = Value::Null,原本期望继承全局 config 的节点拿到空配置。

建议DagExecutor.run() 中对 deep_merge 结果做 if result.is_null() { initial_config.clone() } 兜底。

⑲ dag.rs — _ 通配 match 掩盖未覆盖变体

文件: crates/df-workflow/src/dag.rs:191

deep_merge 的 match 最后分支 _ => node.clone() 过于宽泛。若未来新增 serde_json::Value 变体,不会被编译器捕获。

建议:显式列出所有剩余变体并 bail!,迫使维护者在修改时意识到影响。

⑳ shell.rs — String::from_utf8_lossy 编码错误被静默替换

文件: crates/df-execute/src/shell.rs:155-156

shell 输出使用 String::from_utf8_lossy,将非 UTF-8 字节替换为 <EFBFBD>。对于中文/日文环境(如 chcp 65001 前的 GBK 输出),用户看到乱码替代符,诊断困难。

建议:添加 tracing::debug! 记录损失性替换的字节数,或提供 encoding_rs 转码选项。

㉑ query LIKE 搜索 — SQL 通配符未转义

文件: crates/df-storage/src/crud/project_repo.rs:245-246

LIKE ? 使用 %{trimmed}% 拼接,keyword 中的 %/_ 会被字面匹配。用户搜索 100% 匹配所有行,搜索 error_404 匹配 errorX404

建议:对 keyword 中的 %/_replace 转义,加 ESCAPE '\' 子句。

㉒ coordinator.rs — 空壳仅用注释声明"勿删",应加编译期守卫

文件: crates/df-ai/src/coordinator.rs:1-24

注释声明「B 路线占位,有意保留空壳,勿删」,但无 #[deprecated]#[doc(hidden)] 编译期标记。调用方引入 AgentCoordinator::new().run() 只能运行时发现返回 "TODO"

建议:加 #[deprecated(note = "B 路线占位,勿用于生产")]

㉓ secret.rs — 密钥迁移 sidecar 文件写入无原子性

文件: crates/df-storage/src/secret.rs:75-81

record_migration_fail 每次调用同步读文件 → 改 map → 写文件,迁移时 10+ provider 逐条失败时每条 2 次文件 I/O。无原子写入保障异常中断可能产生半写文件。

建议:积累内存中的失败计数,迁移循环结束后一次性持久化。

㉔ Intent/planner/plan_hint — 纯函数模块无单测覆盖

文件: crates/df-ai/src/intent.rs / planner.rs / plan_hint.rs

注释声称「纯函数、零 IO」均无 #[cfg(test)] 覆盖。Phase 1 接入主 loop 前应补充。

建议:为这些纯函数模块补充输入输出确定性匹配测试。


🟡 前端

㉕ Settings.vue — 内联 confirm 弹层重复实现

文件: src/views/Settings.vue:7-16

Settings.vue 内联了完整 confirm 弹层模板,完全复制了 ConfirmDialog.vue 的 UI/逻辑,自身维护 confirmState/answerConfirm。全项目已有 useConfirm composable。

建议:用 useConfirm + ConfirmDialog 替换内联实现。

㉖ 多视图 — 按钮 CSS 重复定义

文件: Dashboard.vue:126-162 / Projects.vue:445-458 / Ideas.vue:421-432 / Knowledge.vue:449-458

四处视图各自定义 scoped 按钮样式。项目已通过 main.ts 导入全局 styles/components.css

建议:删除视图内按钮样式,统一走全局 CSS。

㉗ Ideas.vue + Knowledge.vue — tags 解析逻辑重复

文件: src/views/Ideas.vue:302 / src/views/Knowledge.vue:321

tags.split(',').map(t => t.trim()).filter(Boolean)

建议:提取为工具函数 parseTagsInput 放进 src/utils/

㉘ Ideas.vue — Arco Design Message 残留

文件: src/views/Ideas.vue:144

全项目仅在 Ideas.vue 使用了 Arco 的 Message 组件3 处),其余使用自建 toast。混用。

建议:用自建 showToast 替换后删除 Arco 依赖。

㉙ stores/ai.ts — 两个 watch 监听同一 .messages.length

文件: src/stores/ai.ts:204, 241

两个 watch(() => state.messages.length, ...) 分别处理条数上限和 parts 体积上限。每次 length 变化触发两个 watch第二个还要遍历 messages 求和。

建议:合并为一个 watch。

㉚ AiChat.vue — initDrainQueueListener 每次 useAiStore() 调用都执行

文件: src/stores/ai.ts:304

void initDrainQueueListener()

每次调用 useAiStore() 都执行。需确认内部做了幂等守卫。

建议:确保只初始化一次(如外层 initOnce 布尔守卫)。

㉛ AiChat.vue — 空值传播无告警

文件: src/components/AiChat.vue:238

const conv = store.state.conversations.find(c => c.id === id)
const title = conv?.title || t('aiChat.newConversation')

find 返回 undefined 时静默回退默认标题。非预期 ID 进入时不告警。

建议:至少 console.warn 记录 ID 不在列表中的情况。

㉜ 多视图 — catch (e: any) 类型退化

文件: 几乎所有 Vue 视图

catch (e: any) {
  state.error = e?.toString() ?? t('xxx.failed')
}

建议:统一改为 catch (e: unknown)

㉝ AiChat.vue — 体积仍偏大

文件: src/components/AiChat.vue(模板 128 行 + script ~200+ 行)

仍承载全局快捷键处理、编辑态管理、队列编辑态管理、Toast 共享状态等。

建议:将键盘快捷键抽离为 useAiShortcuts composable。

㉞ Ideas.vue — searchQuery.trim() 重复调用

文件: src/views/Ideas.vue:219-220

if (searchQuery.value.trim()) {
  q.keyword = searchQuery.value.trim()
}

建议:提取 const trimmed = searchQuery.value.trim()

㉟ Projects.vue — 冗余 as string 断言

文件: src/views/Projects.vue:217-218, 222

selected 已被类型收窄为 string,后续 as string 多余。

㊱ MCP tools/list — unwrap_or(Value::Null) 在数组中混入 null

文件: crates/df-mcp/src/server.rs:140-142

serde_json::to_value(&t.tool).unwrap_or(Value::Null)

若某个 tool schema 序列化失败tools 数组中混入 null

建议.filter_map(|t| serde_json::to_value(&t.tool).ok())

㊲ Ideas.vue — IdeaQuery 类型未充分对齐

文件: src/views/Ideas.vue:146, 214-217

order_by 字段类型为 string | null,后端若收窄为联合类型白名单,前端不会报错。

建议:前端 order_by 使用 'score' | 'created_at' | null


亮点

① 密钥管理分层设计secret.rs

resolve_provider_secret 三阶段回落DB 明文(向前兼容)→ OS keyring → 空字符串。迁移非阻断、幂等、失败计数告警。单测通过 cfg gate 仅桌面 OS 跑 keyring 测试。

② SQL 注入防护体系

列名白名单(validate_column_name + impl_repo! 宏统一调用),build_order_clause 对排序方向也做白名单校验。软删除/恢复/清空全部参数化 SQL。

③ 前端竞态保护knowledge.ts

递增序列号丢弃过期响应。ai.ts watch 双重守卫(oldLen > 0 && newLen - oldLen <= 2)精确区分局部增长和整体替换。

useConfirm 统一确认弹层

从 4 个视图中提取重复 Promise+resolve 模式,正确处理并发覆盖场景(前一个 Promise 先 resolve false

⑤ Store 内部分拆 4 子 store

stores/project/ 拆分为 projects/tasks/ideas/workflow共享 state.ts 单例,对外 useProjectStore() 不变——「内部拆分,外部零改动」。

⑥ 取消路径 TOCTOU 三层防护

state.rs:86-100 + executor.rs:180-232Ok 后检查、Err 分支检查、阻塞节点轮询。注释详细记录了 TOCTOU 时序窗口。

⑦ MCP 多层防御

三级过滤:visible() 读模式仅 Low 工具、tools/list 剔除 High、dispatch 再拦截 read-only + Medium 拒 + High 兜底拒。

⑧ V21 大版本迁移谨慎

BATCH_SIZE=50 分页、空库跳过、解析失败 skip 而非崩溃、column_exists 守卫 ALTER TABLE 防重复执行。

⑨ 子进程清理 + Windows 黑窗抑制

kill_on_drop(true) 超时后子进程不残留;creation_flags(0x08000000) 抑制 Windows 黑窗闪现。


📊 汇总

分类 🔴 🟡
安全 8
文档 9
代码质量 7
前端 13
亮点 9
合计 17 20 9

总体评价核心安全架构扎实SQL 注入防护、密钥管理、MCP 多层防御、取消 TOCTOU。主要风险集中在 state.rs 同步锁在 async 上下文中的潜在死锁、文档与代码脱节AI对话引擎工具数过时、INDEX 漏列 9 文档、~100 行已移除模块残留。前端整体质量高但需清理少量遗留Arco 残留、内联 confirm 重复)。