Skip to content

上游 deer-flow 安全 / 数据正确性 fix backport 清单

审计师:资深发布工程 / 安全维护审计师(脚本式执行,只读) 日期:2026-05-18 冻结基线:上游 898f4e8(2026-04-17 12:00,a-cdm fork 点) 目标区间:898f4e8..39f901d3(上游 main @ 2026-05-17)= 199 提交(197 非 merge + 2 merge) Ground truth:/Users/a13895184740/repos/a-cdm/deer-flow(逐路径 ls 核实) 背景输入:a-cdm-vs-deer-flow-差异报告.md二开实现合理性审计.mda-cdm/CLAUDE.md §0(vendored-sync 策略)


0. 执行摘要

199 提交分流统计

档位条数说明
P0 必补6数据丢失 / 安全漏洞 / 沙箱逃逸,文件在 a-cdm 适用,且多数 cherry-pick 冲突面低
P1 应评估17正确性 / 稳定性 / 隔离 fix,适用但影响中等或冲突面较高需人工移植
P2 可不跟174feature(28)/ 上游已被 a-cdm 替换区域(persistence·langgraph_auth·Aegra 替换的 runtime-store ≈ 40+)/ 依赖 bump(13)/ docs+CI(20+)/ 纯前端体验(9)/ 上游内部 refactor(7)/ 其它低价值 fix

注:fix 前缀 124 条里大部分落在 a-cdm 已替换或不存在的子系统(persistence/runtime/journal.pyruntime/events/app/gateway/auth/langgraph_auth.py),这是 P2 占比高达 88% 的结构性原因 —— a-cdm「新增外挂目录、不侵入上游核心」的纪律使绝大多数上游 fix 与它无关。

一句话风险结论

a-cdm 当前最致命的暴露是 1 个 [security]-tagged 的主机侧文档转换攻击面(#2332,上传即触发,a-cdm uploads.py:183 实锤调用)+ 3 个本地沙箱逃逸 + 1 个 setup-agent 数据删除;这 5 条都落在 a-cdm 真实存在且真实暴露的文件上、且都在上游持久化大重构 #2153 之前(冲突面低),应优先 cherry-pick。a-cdm 因「用 Aegra 替换 LangGraph Server runtime + 自建 acdm_auth + 无 persistence 子系统」而结构性免疫了上游约 40+ 条 persistence/auth/runtime-store fix(这些文件在 a-cdm 根本不存在)。

关键方法论事实(决定本清单有效性)

经 ground truth ls 核实,以下子系统在 a-cdm deer-flow/backend 不存在,触及它们(且仅触及它们)的上游 fix 一律 N/A:

不存在路径原因受影响上游 fix(N/A 大类)
packages/harness/deerflow/persistence/#1930/#2134/#2153 fork 后新增,a-cdm 用 Aegra 持久化(platform-db aegra DB)全部 fix(persistence)persistence/run/sql.pypersistence/thread_meta/*(如 #2910 #2865 #2989-store 部分)
runtime/journal.pyruntime/events/同上,fork 后随统一持久化层引入#2762 #2850 #2593(journal 部分)#9892a7d4(journal 部分)
runtime/runs/store/runtime/user_context.pyfork 后新增#2962(部分)#2867(user_context 部分)
app/gateway/auth/langgraph_auth.pycsrf_middleware.pyauth_middleware.pyinternal_auth.pyauthz.pya-cdm 自建 acdm_auth.py(SDK Auth 扩展点)+ acdm-backend OAuth2,完全不用上游 Gateway 鉴权#2740 #2933 #2593(auth 部分)#2008 及 da174dfd/ed9ebfac 等全部 Gateway-auth fix
tools/sync.pyskills/tool_policy.pyskills/storage/config/runtime_paths.pymiddlewares/dynamic_context_middleware.pymiddlewares/tool_call_metadata.pysubagents/token_collector.pytools/builtins/update_agent_tool.py均 fork 后新增对应 fix 中针对这些文件的部分 N/A;若 fix 同时改了存在的文件则降级评估

反之,以下 a-cdm 真实存在且真实暴露,触及它们的 fix 才进 P0/P1:sandbox/*(local 沙箱,config.yaml 实锤启用)、uploads/manager.py + app/gateway/routers/uploads.py(实锤调用 file_conversion)、skills/installer.py/parser.pymcp/cache.pymodels/claude_provider.pyagents/middlewares/*(17 个)、agents/memory/*tools/builtins/setup_agent_tool.py/task_tool.pysubagents/executor.pyagents/lead_agent/agent.pyruntime/runs/{manager,worker}.py(Gateway 8001 仍 import 使用,见 services.py:21-31)。


1. P0 必补清单(数据损坏 / 安全漏洞 / 沙箱逃逸,适用且应尽快补)

#hash原始 message改动路径(核心)a-cdm 适用性证据为何 P0cherry-pick 冲突预判
P0-180e210f5[security] fix(uploads): require explicit opt-in for host-side document conversion (#2332)app/gateway/routers/uploads.pyharness/deerflow/utils/file_conversion.pyconfig.example.yaml实锤暴露:a-cdm app/gateway/routers/uploads.py:26 import convert_file_to_markdown,:183upload_files默认调用主机侧转换。两文件均 EXIST主机侧文档转换(markitdown 等)是上传即触发的主机解析攻击面(恶意文档→主机侧库 RCE/DoS);上游标记 [security],改为显式 opt-in。a-cdm 默认开着PRE-#2153,低冲突。注意 a-cdm uploads.py 已被 acdm_thread_auth 侵入(routers/uploads.py import require_thread_owner),cherry-pick 时保留该 import + Depends
P0-26bd88fe1fix(sandbox): block host bash traversal escapes (#2560)harness/deerflow/sandbox/tools.py (+278 行)a-cdm config.yaml 启用 deerflow.sandbox.tools(local 沙箱,aio_sandbox 注释掉);sandbox/tools.py EXIST 且冻结点 1345 行与 a-cdm 一致本地沙箱 bash 路径守卫可被 .. 遍历绕过逃出 thread 数据区 → 跨租户文件越权 / 主机文件读写。a-cdm 多租户,lead_agent 默认带 bashPRE-#2153,低冲突(sandbox/tools.py 在 a-cdm 与冻结点同为 1345 行,#2153 大重构未进 a-cdm,可干净 apply)
P0-339c5da94fix(sandbox): prevent local custom mount symlink escapes (#2558)sandbox/local/list_dir.pysandbox/local/local_sandbox.py两文件 EXIST;a-cdm 用 local 沙箱自定义挂载点 symlink 逃逸 → 沙箱外主机路径读写(沙箱逃逸,安全漏洞)PRE-#2153,低冲突
P0-4af8c0cfbfix(harness): constrain view_image to thread data paths (#2557)agents/factory.pysandbox/tools.pytools/builtins/view_image_tool.py三文件 EXIST;view_image_tool 是 lead_agent 默认工具view_image 未约束到 thread 数据路径 → 任意主机文件经图像工具读出回传 LLM(信息泄露 / 越权读)PRE-#2153,低冲突。agents/factory.py 仅 6 行改动,注意 a-cdm 该文件无定制(create_deerflow_agent 原样)
P0-5707ed328fix(skills): scan skill archives before install (#2561)app/gateway/routers/skills.pyskills/__init__.pyskills/installer.py (+111 行)三文件 EXIST;a-cdm 有 skills 安装链路(skills/installer.py、Gateway routers/skills.py)安装 skill 归档前不做安全扫描 → 恶意 skill 包(任意脚本/SKILL.md)安装即代码执行风险PRE-#2153,低冲突。注意 a-cdm skills/security_scanner.py 存在但版本旧(见 P1-1 #2987 配套)
P0-6fc94e90ffix(setup-agent): prevent data loss when setup fails on existing agent directory (#2254)tools/builtins/setup_agent_tool.pyEXIST,冻结点已有此文件;a-cdm 项目/用户 agent provision 走 setup_agent(project_agent_service → Gateway POST /api/agents)setup 失败时无差别 shutil.rmtree 删除 pre-existing SOUL.md/config.yaml → 已有 agent 配置被静默销毁(数据丢失)。a-cdm project-{slug}/user-{sub} agent 重复 provision 时命中PRE-#2153,极低冲突(单文件 11 行,纯 is_new_dir 守卫,无依赖)

P0 共性:全部 PRE-#2153(上游持久化大重构 2e05f380 之前),意味着它们不依赖 a-cdm 没拿的持久化层,可基本干净 cherry-pick。建议按 P0-6 → P0-2/3/4 → P0-5 → P0-1 顺序补(冲突面从低到高;P0-1 因侵入了 acdm_thread_auth 的 uploads.py 留最后人工核对)。


2. P1 应评估清单(正确性 / 稳定性 / 隔离,适用但影响中等或需人工移植)

#hashmessage路径适用性P1 理由冲突预判
P1-1a814ab50fix(skills): make security scanner JSON parsing robust (#2987)skills/security_scanner.pyEXIST安全扫描器对 LLM 输出变体解析不稳健 → 扫描静默失败 = P0-5 的扫描形同虚设;与 P0-5 配套补POST-#2153,中(scanner 可能随 #2613 skills 重构演进)
P1-2722c690ffix(memory): isolate queued memory updates by agent (#2941)agents/memory/queue.pysummarization_hook.pyEXIST,a-cdm queue.py 已有 agent_name 字段但 debounce 仅按 thread_id多 agent / 多用户共 thread 时排队记忆更新跨 agent/user 串写(隔离缺陷,a-cdm 强多租户)。窗口短故 P1 非 P0POST-#2153,中(fix 引入 user_id 入 queue identity,a-cdm 冻结 queue.py 可能无 user_id 需适配)
P1-368d8caecfix(agents): make update_agent honor runtime.context user_id like setup_agent (#2867)runtime/user_context.py(ABSENT)、tools/builtins/setup_agent_tool.py(EXIST)部分适用contextvar 缺失时 agent 写入 users/default 污染共享桶并丢用户编辑(数据损坏类)。a-cdm 后台/跨进程 provision 命中;但核心 helper 在 ABSENT 的 user_context.py → 需把 resolve 策略人工移植进 a-cdm setup_agent高(依赖 ABSENT 文件,非干净 pick,需重写)
P1-41c96a6affix: keep new agent bootstrap in user scope (#2784)app/gateway/services.pysetup_agent_tool.pyEXIST同类:新 agent bootstrap 落错 user scope(隔离/数据正确性);P1-3 的前序中-高,需与 P1-3 一起评估
P1-5e543bbf5[security] fix(upload): reject symlinked upload destinations (#2623)app/channels/manager.pyrouters/uploads.pyuploads/manager.pyEXIST上传目标 symlink 未拒绝 → 经 symlink 写到沙箱外主机路径(安全)。本应 P0,降 P1 因 POST-#2153 冲突面高且依赖 #2153 路径解析重构高(POST-#2153,uploads/manager.py 经 #2153 重构,需人工移植安全检查逻辑)
P1-60d1053cafix(uploads): add Windows support for safe symlink-protected uploads (#2794)uploads/manager.pyEXISTP1-5 的跨平台补全;若补 P1-5 应连带高(同 P1-5 链)
P1-774081a85[security] fix(sandbox): bind local Docker ports to loopback (#2633)community/aio_sandbox/local_backend.pyEXIST 但 a-cdm config.yaml aio_sandbox 注释掉(用 local 沙箱)docker 端口默认 0.0.0.0 暴露(安全);a-cdm 当前不启 aio_sandbox 故暴露面低,但留作启用前置POST-#2153,中
P1-8f7dfb88afix(aio-sandbox): redact env values in container logs (#2562)community/aio_sandbox/local_backend.pyEXIST,aio_sandbox 未启用容器日志泄露 env 凭据(信息泄露);同 P1-7 仅启用 aio_sandbox 才暴露
P1-9ca1b7d5ffix(sandbox): add missing path masking in ls_tool output (#2317)sandbox/tools.pyEXIST,local 沙箱启用ls_tool 未脱敏 → 主机绝对路径泄露给 LLM(信息泄露,弱)。3 行改动PRE-#2153,低(可与 P0-2/3/4 同批)
P1-10cef42243fix(skills): enforce allowed-tools metadata (#2626)lead_agent/agent.pylead_agent/prompt.pyskills/parser.pyskills/validation.pysubagents/executor.py(+skills/tool_policy.py ABSENT)部分适用skill 的 allowed-tools 元数据未强制 → skill 可越权调未授权工具(权限绕过)。但核心策略文件 tool_policy.py ABSENT + 改 lead_agent/prompt.py(a-cdm 重度定制注入 ~300 行)→ prompt.py 必冲突很高(prompt.py 是 a-cdm 最大侵入点,需人工三方合并)
P1-11f9ff3a69fix(middleware): avoid rescuing non-skill tool outputs during summarization (#2458)lead_agent/agent.pysummarization_middleware.pyconfig/summarization_config.pyEXIST摘要中误"抢救"非 skill 工具输出 → 上下文污染 / 正确性中(agent.py 经多次 app_config 重构演进)
P1-12181d8365fix(middleware): normalize tool result adjacency before model calls (#2939)dangling_tool_call_middleware.pyEXISTtool result 邻接未规范化 → 模型调用 400 / 对话损坏(稳定性)POST-#2153,中
P1-1320d2d2b3fix(middleware): Handle invalid tool calls in dangling pairing middleware (#2890)dangling_tool_call_middleware.pyEXIST非法 tool call 致中间件崩 → run 失败(稳定性);与 P1-12 同文件应合并评估
P1-140c37509bfix(middleware): Prevent todo completion reminder IMMessage leak (#2907)todo_middleware.pyfrontend/.../messages/utils.tsEXIST内部 IMMessage 泄露进对话(信息泄露,弱)+ 前端配套POST-#2153,中
P1-1524fe5fbdfix(mcp): prevent RuntimeError from escaping except block in get_cached (#2252)mcp/cache.pyEXISTMCP 缓存 except 块 RuntimeError 逃逸 → MCP 工具不可用(稳定性);a-cdm 重度用 MCP(knowledge BFF)PRE-#2153,低(单文件)
P1-161f59e945fix: cap prompt caching breakpoints at 4 to prevent API 400 errors (#2449)models/claude_provider.pyEXISTprompt cache 断点 >4 → Anthropic API 400 整个 run 失败(稳定性,a-cdm 用 claude/火山方舟)PRE-#2153,低(单文件 provider)
P1-1717447fccfix(runtime): make rollback restore checkpoint supersede newer checkpoints (#2582)app/gateway/routers/threads.pyruntime/runs/worker.pyEXIST(Gateway 仍用 runtime/runs);但 a-cdm 生产 runtime-of-record 是 Aegra,rollback 多走 Aegrarollback 后旧 checkpoint 未压过新的 → 回滚后状态错乱(数据正确性)。置信度低:a-cdm 实际 rollback 路径是否经 Gateway runtime 未确证POST-#2153 高;先确认 a-cdm 是否暴露此路径再评估

P1 取舍指南:P1-1(配套 P0-5)、P1-9、P1-15、P1-16 冲突面低、价值清晰,建议与 P0 同批补;P1-2/3/4(隔离/数据正确性)价值高但需人工移植,排第二批;P1-5/6/10/17 冲突面高或暴露面存疑,先评估再决定,不盲补。


3. P2 批量归类(174 条,各桶一句话带过,不逐条)

约条数一句话理由(为何不跟)
a-cdm 已替换/不存在子系统的 fix~50触及 persistence/(#2910 #2865 #2850 #2989 #2134 #1930 #2153 #56d5fa33 等)、runtime/journal.py/events/(#2762 #9892a7d4-journal 部分)、runtime/runs/store/(#2962)、app/gateway/auth/+langgraph_auth.py+csrf/auth/internal-middleware(#2740 #2933 #2593 #2008 #2063 da174dfd ed9ebfac 4e4e4f92 等)—— 这些路径在 a-cdm ls 不存在(Aegra 替换 runtime + acdm_auth 替换鉴权 + 无持久化层),物理上无处可 apply,N/A
feature(新功能,非修复)28Discord/DingTalk/Serper/Slack channels 增强、token-usage 展示模式、自定义 agent self-update、MindIE provider、subagent skill loading、Playwright E2E 等 —— a-cdm 冻结策略只跟 fix 不跟 feature
依赖 bump13chore(deps) next/urllib3/langsmith/lxml/dompurify/uuid 等 —— a-cdm 应走自己的依赖审计节奏,不随上游 bump(除非 bump 修 CVE;本批无明确 CVE 标注)
docs + CI + 构建脚本~22docs:(7)、CI build/lint 修复、pre-commit hooks、wait-for-port.sh、prettierignore、container push workflow、Apple Container 语法等 —— 不影响运行时正确性/安全
纯前端体验 fix9fix(frontend) 登录闪烁/resize loop/命令面板 hydration/thread 去重/首消息吞没/HTML 嵌套 等 —— a-cdm 前端已重度定制(src/core/acdm/ 27 模块 + workspace),上游前端 fix 多落在 a-cdm 未用或已改的页面/hook,逐条价值低;若某条命中 a-cdm 仍用的原生 core/threads/core/messages 可个案捞(如 #2958 #2731 #2964),否则不跟
上游内部 refactor7refactor: thread app_config/release config through ...(#2666 #2652 #2612 #2611 8ba01dfd 38714b6c b8bc4826)—— 纯上游架构演进,无行为修复,且会大面积改 lead_agent/agent.py 与 a-cdm 定制冲突,明确不跟
低价值/边缘 fix~45debug.py Pydantic warning、uv extras、locale、msys 路径、CI lint、token usage 显示去重、clarification idempotent、event loop 优化(#2627 #2414,a-cdm Aegra 跑 graph 不一定命中)等 —— 适用但影响低,记账即可不主动补;个别(#2309 httpx.ReadError、#2350 clarification 幂等、#2331 lifespan 关停)可视稳定性需要个案捞

P2 不是"逐条确认无害",而是"按桶归因到不适用 / 非修复 / 低价值"。维护者每月只需对新增提交重跑分流,P2 桶规则可直接复用。


4. 可持续每月甄别 runbook(给维护者的 SOP)

落地位置:把本节固化进 docs/runbooks/vendored-sync.md;已评估提交账本同文件维护。

4.1 每月 SOP(约 30–45 分钟)

bash
# 0. 准备:确定上次评估的上游 commit(从 vendored-sync.md 账本读 LAST_EVALUATED)
LAST=<上次记录的上游 hash>           # 首次 = 39f901d3
cd /Users/a13895184740/repos/deer-flow && git fetch origin
HEAD_NOW=$(git rev-parse origin/main)

# 1. 拉增量(只看 fix/security/perf,过滤 feat/docs/chore/refactor/test)
git log --oneline --no-merges $LAST..$HEAD_NOW \
  | grep -iE 'fix|\[security\]|perf' \
  | grep -ivE 'fix\(frontend\)|fix\(docs|fix\(ci|chore|^[0-9a-f]+ docs'

# 2. 关键词二筛(只深看命中安全/数据/隔离/崩溃的)
git log --oneline --no-merges $LAST..$HEAD_NOW \
  | grep -iE 'security|CVE|auth|leak|corrupt|data loss|symlink|escape|traversal|isolat|race|crash|persist|sandbox|owner_sub|delete'

# 3. 对每条疑似项看改动路径
git show --stat --format='%s%n%b' <hash> | grep -E '\|'

# 4. 回 a-cdm ground truth 判适用性(决策树见 4.2)
for p in <改动路径>; do
  [ -e /Users/a13895184740/repos/a-cdm/deer-flow/backend/$p ] && echo "EXIST $p" || echo "ABSENT $p"
done

# 5. 判断与冻结点持久化重构的先后(冲突预判)
git merge-base --is-ancestor 2e05f380 <hash> && echo "POST-#2153 冲突面高" || echo "PRE-#2153 冲突面低"

# 6. 记账(见 4.3),更新 LAST_EVALUATED = $HEAD_NOW

4.2 适用性决策树(每条 fix 必走)

  1. 改动路径是否全部命中 §0「不存在路径」表? → 是 → 标 N/A(已替换/不存在),记账一行带过。
  2. 路径在 a-cdm EXIST → 回 a-cdm 实际确认是否真暴露:
    • sandbox/*?→ a-cdm config.yaml 启用 deerflow.sandbox.tools(local),真暴露 → 进 P0/P1。
    • app/gateway/auth/*/langgraph_auth.py/csrf/internal?→ a-cdm 用 acdm_auth,N/A
    • app/gateway/routers/uploads.py 的 conversion?→ a-cdm uploads.py:183 实锤调用,真暴露
    • runtime/journal.py/events//persistence//runs/store/?→ N/A(Aegra 持久化)。
    • runtime/runs/{manager,worker}.py?→ Gateway 8001 仍 import(services.py:21),部分暴露,需个案确认走 Gateway 还是 Aegra。
  3. 价值分档:数据丢失/安全/沙箱逃逸/越权 + 真暴露 + PRE-#2153 → P0;正确性/隔离/稳定性 或 POST-#2153 高冲突 → P1;feature/refactor/已替换/低价值 → P2
  4. 冲突预判:PRE-#2153 = 低(可直接 git cherry-pick);POST-#2153 或触及 lead_agent/prompt.py/agents/lead_agent/agent.py/langgraph.json/uploads.py(acdm_thread_auth 侵入处)= 高,必须人工三方合并并复核 a-cdm 定制不被覆盖。

4.3 记账规范(防重复评估)

docs/runbooks/vendored-sync.md 维护两个表:

  • LAST_EVALUATED:一行,记录"已评估到的上游 commit hash + 日期"。下次从这里增量,不重扫历史。
  • 评估账本表:每条评估过的提交一行 | hash | message | 判定(P0/P1/P2/NA) | 适用性依据 | 状态(已cherry-pick <本地commit> / 已评估不补 / 待补) |P2/NA 也要记(否则下次又重判)。已 cherry-pick 的额外记 a-cdm 侧的 commit hash,便于追溯。

4.4 防回退红线(承接背景报告建议 #1,强烈建议立即做)

cherry-pick 上游若动到 langgraph.json/aegra.json,必须人工确认 auth.path 仍指 ./packages/harness/deerflow/auth/acdm_auth.py:auth。建议加一行 CI 断言(实测当前无此保护):

python
import json
for f in ("deer-flow/backend/langgraph.json", "deer-flow/backend/aegra.json"):
    assert json.load(open(f))["auth"]["path"].endswith("acdm_auth.py:auth"), f"{f} auth.path 被回退!owner_sub 隔离将失效(越权)"

理由:上游这两文件持续演进,cherry-pick 同文件会静默把鉴权回退到上游 langgraph_auth.py → owner_sub 多租户隔离失效 = 跨用户数据越权。这是本次审计认定的最高优先非 fix-backport 类风险


5. 方法论与置信度声明

  1. 适用性一律回 a-cdm ground truth:所有"存在/不存在"判定均以对 /Users/a13895184740/repos/a-cdm/deer-flow/backend 逐路径 ls/-e 实测为准(本报告 §0 表、§1/§2 证据列),不臆测。"真暴露"另以 a-cdm 源码实锤(如 uploads.py:183 调 file_conversion、config.yaml 启用 local sandbox、services.py:21 import RunManager)。
  2. N/A 判据:文件不存在(fork 后上游新增)或 a-cdm 已用定制件取代该子系统(Aegra 替换 LangGraph Server runtime/store/journal/persistence;acdm_auth 替换 langgraph_auth + Gateway-auth 全家)。这与两份背景报告"a-cdm 新增外挂目录、不侵入上游核心"的结论一致并互为印证。
  3. 冲突预判依据:以 git merge-base --is-ancestor 2e05f380(#2153 持久化大重构) <hash> 判 PRE/POST —— a-cdm 未拿 #2153,PRE 的 fix 基本可干净 apply,POST 的常带 #2153 路径解析依赖需人工移植。另:触及 lead_agent/prompt.py(a-cdm 注入 ~300 行)与 acdm_thread_auth 侵入的 routers/{threads,uploads,artifacts}.pylanggraph.json/aegra.json 一律标高冲突。
  4. 置信度分级:P0 全部【确证】(路径存在 + 真暴露实锤 + 漏洞性质明确 + PRE-#2153);P1 多数【确证】,P1-17(rollback 是否经 a-cdm Gateway 路径)、P1-2(冻结 queue.py 是否已含 user_id 字段)标【需二次确认】;P2 按桶【合理推断】,未逐条深挖(符合任务"不逐条平均用力"约束)。
  5. 未深挖项的处理:P2 的 174 条按子系统归因到 N/A / 非修复 / 低价值,不逐条 git show;若维护者对某 P2 桶内具体条目存疑,按 §4.2 决策树个案复核即可,不影响 P0/P1 清单有效性。
  6. 本报告只读:未执行任何 cherry-pick/checkout,未改任何代码或 git 状态,仅产出本文件。

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析