首页
/ DeepTutor v1.4.1 技术解读:TutorBot 工具沙箱加固、按用户资源隔离与多模态图片回退

DeepTutor v1.4.1 技术解读:TutorBot 工具沙箱加固、按用户资源隔离与多模态图片回退

2026-09-08 22:40:32作者:翟江哲Frasier

**DeepTutor v1.4.1(2026.05.27 发布)**是紧随 v1.4.0 GA 推出的"安全 + 稳定性"补丁版本,定位为 drop-in 升级:关闭 TutorBot 的默认 shell 执行入口、落实按用户数据隔离、修复 v1.4.0 引入的聊天输入回归,并新增面向特定 TutorBot 的 HTTP/SSE 对话 API 与多模态图片降级策略。读完本篇,你可以完整掌握该版本的安全加固面、需要感知的破坏性变更(尤其是 allow_shell_exec 语义)、新增 API 的用法,以及 ZIP 安全解压与网络设置背后的工程细节,并能结合仓库源码验证每个结论。

版本定位与升级路径

v1.4.1 的全部改动建立在 v1.4.0(2026.05.22 GA)基础上,属于同一条 v1.4 线的补丁发布。官方给出的升级方式是 drop-in(无缝替换)

# pip 安装用户直接升级
pip install -U deeptutor

# Docker 用户拉取最新镜像
docker pull ghcr.io/hkuds/deeptutor:latest

需要特别注意的是,本次发布包含默认行为变更:TutorBot 的 shell exec 能力从"默认开启"改为"默认禁用",升级后必须显式配置才能恢复(详见下文),因此即使是补丁版本,也建议在升级前核对现有 TutorBot 对 shell 工具的依赖情况。

从仓库的发布记录目录 assets/releases/past_releases 可以看到,v1.4.1 之后该线还持续迭代到 v1.4.15,v1.5.0 之后相关机制又做了进一步演进(例如 allow_shell_exec 在自动启动时被静默复位为 false 的问题 #531、无法持久化的问题 #534,均在 v1.4.x 后期修复)。下文会同时给出当前仓库中仍可核验的源码证据。

TutorBot 工具沙箱:shell exec 改为显式 Opt-In

这是 v1.4.1 最核心的安全变更,直接回应了多个高危漏洞(#518、#506)。变更要点如下:

  • exec 工具不再默认注册。除非管理员为对应 bot 显式设置 allow_shell_exec=true,否则 shell 执行工具不会出现在工具清单中,LLM 无法调用它。
  • 文件系统与 shell 访问默认收敛到 bot 工作区(workspace)内,工具不能越出该目录读写。
  • 命令 deny-list 在"命令边界"处重新锚定(re-anchored at command boundaries),避免此前"子串匹配"带来的绕过窗口——例如把危险命令嵌入到参数、别名或拼接串中。
  • allow_shell_exec 不能经由 update payload 翻转:即使某个 API 请求携带修改 bot 的 payload,也无法远程把该开关打开,杜绝了"先写入恶意配置再通过更新接口开启执行"的攻击链。

源码侧佐证:Deny 列表与执行链路

当前仓库中仍保留了同类防护的演进形态。聊天侧 shell 工具 deeptutor/tools/exec_tool.py 的模块注释明确写道:它是 TutorBot ExecTool 的 chat 侧对应物,每条命令都经由 deeptutor.services.sandbox 沙箱层执行(而非裸 subprocess),并把 deny 模式作为 OS 隔离之上的纵深防御(defence-in-depth)复用。

该文件顶部保留了与 TutorBot ExecTool 一致的 _DENY_PATTERNS 拒绝模式表,可以作为"命令边界锚定"的实际内容参考:

拒绝模式(正则) 拦截目标
\brm\s+-[rf]{1,2}\b 递归强制删除
\bdel\s+/[fq]\b / \brmdir\s+/s\b Windows 递归删除
`(?:^ [;&|]\s*)format\b`
\b(mkfs|diskpart)\b 磁盘管理
\bdd\s+if= 块设备覆写
>\s*/dev/sd 直接写裸设备
\b(shutdown|reboot|poweroff)\b 系统关机重启
:\(\)\s*\{.*\};\s*: fork 炸弹
`(?:^ [;&|]\s*)(useradd|usermod|passwd|chpasswd|crontab)\b`

注意正则中大量使用 \b(词边界)以及 ^|[;&|] 作为命令起始锚点,这正是"在命令边界锚定"的实现方式——format 只在与命令边界相邻时才被拦截,避免误伤 format 字符串 之类的正常用法。命令默认超时 30 秒、上限 300 秒(_DEFAULT_TIMEOUT = 30_MAX_TIMEOUT = 300,见 exec_tool.py)。

执行时工具只负责构造 ExecRequest(含 commandworkdirmountsResourceLimits),真正的隔离由沙箱服务完成:当前轮次的工作区被 read-write 挂载为工作目录,_sandbox_user_id_sandbox_workdir_sandbox_mounts 等参数由服务端管线注入,LLM 永远无法自行提供,从而确保隔离边界掌握在平台侧而非模型侧。

升级动作

如果升级前确实依赖 TutorBot 的 shell 执行能力,需要在 bot 配置上显式开启:

allow_shell_exec = true

即便开启,工具访问范围仍被限制在 workspace 之内(沙箱挂载边界 + deny-list 双保险),不建议为了省事关闭这一层。

按用户资源隔离:目录、数据库与会话全链路收敛

v1.4.1 把多用户边界从"登录鉴权"下沉到"存储与运行时资源"层面,明确要求跨用户请求无法触达彼此数据。官方宣布隔离的清单包括:

  • Book 根目录(教材/书目数据根);
  • 会话数据库(session databases);
  • turn-runtime 存储(每轮对话的运行时状态存储);
  • TutorBot 目录

并且 web/API 会话按 session 粒度 key 化——同一用户的不同会话之间、不同用户之间均不可互相读写。

当前仓库中的演进形态

从当前仓库源码看,这一隔离设计在后续版本中被持续固化并迁移:deeptutor/multi_user/paths.py 定义了清晰的目录拓扑:

data/user             # admin 工作区根(管理员的根即 data/)
data/users/<uid>      # 每个非 admin 用户独立工作区
data/partners/<id>    # partner(合成用户)工作区
data/system           # 系统级数据

其中 user-secrets 等按 owner 隔离的机密目录永不挂载给运行容器。文件中 migrate_legacy_multi_user_tree() 说明早期(v1.4.x 时代)的 "sibling multi-user/ 树"(即 PROJECT_ROOT/multi-user 下的 _system 与各 uid 目录)会在升级时一次性迁移到 data/ 布局——这与 v1.4.1 发布说明中"按用户隔离"落地于 deeptutor/multi_user/ 体系的历史实现相互印证。

配套的隔离面还包括权限模型层:deeptutor/multi_user 下的 grants.py(授权)、context.py(当前用户上下文)、identity.py(身份)、paths.py(路径解析)协同工作;对应测试集中在 tests/multi_user,例如 test_resource_isolation.py(资源隔离)、test_owner_bound_models.py(owner 绑定的模型)。v1.4.1 修复的多个 authz bypass(#516 跨 bot 文件管理、#515 跨会话 turn-regeneration、#514 book-confirmation)正是这类边界上的历史漏洞。

面向指定 TutorBot 的 HTTP / SSE 对话 API

应社区功能请求 #511,v1.4.1 为"与某一个特定 TutorBot 进行多轮对话"新增了两组外部客户端可用的端点:

  • POST /{bot_id}/chat —— 发起一轮对话,返回回合结果;
  • POST /{bot_id}/chat/execute-stream(SSE)—— 流式执行,逐事件推送推理/执行进度与增量内容。

两者共同具备两个关键行为:

  1. auto-start:bot 尚未启动时,请求会先拉起该 bot 再处理对话,调用方无需关心生命周期;
  2. 持久 per-session 上下文:对话状态按会话持续保存,支持真正的多轮上下文,而非"一问一答"的无状态接口。

这套接口把 TutorBot 从"仅平台内可交互"扩展为"可被外部系统集成",例如通过脚本、定时任务或其他应用持续调用同一个 bot。

当前仓库中的对应实现痕迹

在当前仓库中,"bot" 抽象已向 partner / 通用 capability 方向演化,但实现骨架仍可对照:

  • deeptutor/api/routers/plugins_api.py 提供 SSE 流式执行端点 /tools/{tool_name}/execute-stream/capabilities/{capability_name}/execute-stream(内部由 _execute_stream() 生成 SSE 事件流);
  • 同一文件里 CapabilityExecuteRequest 仍保留 bot_id 字段并注明是 legacy TutorBot 字段名,现用于寻址 partner——从源码注释可以确认 v1.4.1 时期 {bot_id} 寻址的正是当时所称的 TutorBot;
  • deeptutor/partners/channels/base.py 等渠道层沿用了 TutorBot 时代的执行管道,deeptutor/agents/chat/agentic_pipeline.py 中仍可见 tutor 相关命名的 pipeline。

若你仍运行 v1.4.1,直接按文档使用 POST /{bot_id}/chat/chat/execute-stream 即可;若已升级到更高版本,请以对应版本的 partner 路由与命名迁移说明为准(可对照 ver1-5-0.md 之后的发布说明)。

多模态图片回退:不再"静默丢图"

v1.4.1 修复了一类隐蔽的模态兼容性问题:Doubao / VolcEngine 等具备多模态能力的模型,因为没有在平台的 known-vision allowlist 中登记 capability 条目,此前携带图片的请求会被静默丢弃图片。新策略把"图片是否被接受"从"平台预判"改为"运行时验证 + 自动降级":

  1. 乐观注入(Stage 1):图片无条件附加到请求中发给每一个 provider/model——即使平台没有该模型的视觉能力条目,也不做前置拦截;
  2. 失败降级(Stage 2):如果携带图片的请求确实失败、且报错属于"模型不支持图片输入"、同时该模型不在已知视觉 allowlist 中,则把消息中的图片部分剥除,以纯文本重试该轮,而不是让整轮硬失败或静默返回错误结果。

关键设计细节是:已知支持视觉的 allowlist 模型不会走这条降级路径——因为它们理应支持图片,若请求失败就应当暴露真实错误,而不是"看似成功却返回了缺失图片信息后的纯文本答案",从而避免误导。

源码侧佐证:两级回退真实存在

当前仓库中该机制被完整保留并文档化。图片注入入口 deeptutor/services/llm/multimodal.pyprepare_multimodal_messages() 注释写明:

Images are injected optimistically for every provider/model — this function does not consult supports_vision. … the Stage-2 fallback strips the images and retries as text-only.

其中还提及"正是最初的 Doubao/VolcEngine bug"催生了该设计。消息会被转换为 content-parts 数组(原始文本 + 图片),仅当附件为 url-only、而 provider 既不接受 URL 形式又无法解析为本地字节时才在该阶段丢弃(计入 url_images_dropped)。

降级执行点位于 deeptutor/core/agentic/labeled_step.pyrun_labeled_step 重试缝隙中:检测到 is_image_input_unsupported(exc)should_degrade_to_text(binding, model, messages) 成立时,调用 strip_image_parts_inplace(messages) 原地剥除图片(避免后续循环迭代再次携带),并通过 stream.progress 发出 "Model does not support image input; retrying without images." 的警告事件,最终重试为纯文本请求。should_degrade_to_text 的判断依据 supports_vision 是否在 allowlist 中登记,这条语义在 deeptutor/services/llm/provider_core/base.py 的 Stage-2 注释里同样得到确认。

安全 ZIP 上传与网络设置页

ZIP 知识库上传:逐成员校验,拒绝 Zip Slip 与 Zip Bomb

v1.4.1 为 .zip 知识库上传引入了完整的安全解压管线,核心原则是:压缩包逐成员(member-by-member)流过文档校验器,压缩包本身永不进入索引——只有通过校验的成员文档才会被纳入知识库。

校验边界在仓库中有精确实现,见 deeptutor/utils/archive_extractor.pysafe_extract_zip()

防护项 实现要点
Zip Slip(路径穿越) 成员名统一折叠为清洗后的 basename,破坏 ../ 与绝对路径攻击;写入前用 _is_within() 再次校验目标在根目录内
Zip Bomb(条目爆炸) max_entries 限制压缩包内非目录成员总数,超限即拒绝
压缩比异常 单成员 file_size / compress_size 超过 max_compression_ratio(默认 200.0)即判定可疑并跳过
解压超量 单成员按声明大小设置硬字节预算,流式写出时一旦超过即中止并报 "decompressed past its declared size"
系统文件 跳过 __MACOSX/ 前缀与点开头(dotfile)成员
同名冲突 扁平化后重名的成员被跳过(记录原因),避免覆盖

模块注释明确点出了设计动机:常规解压实现既对 Zip Slip 无防护(可通过 ../ 或绝对路径写出任意文件),也"乐意写出任意文件类型";而该实现逐成员流式解压并施加上述全部约束。相关测试见 tests/utils/test_archive_extractor.py,与知识库路由 deeptutor/api/routers/knowledge.py 中的 ZIP 上传链路(配合 deeptutor/utils/document_validator.py)配套工作。

/settings/network:端口、API 基址与 CORS 归一化

新网络设置页把这些平台侧参数集中呈现、集中修改:

  • 监听端口公开 API 基址(public API base);
  • CORS origins:新增归一化逻辑,容忍 host:port 写法与结尾斜杠差异,避免"配置看起来一样但实际不匹配"的经典问题;
  • cookie 安全属性cookie_securecookie_samesite(当 cookie_secure=true 时同站点策略为 none,并汇总出 cross_site_cookie_ready 状态);
  • "fetch models" 动作:从 OpenAI 兼容端点拉取模型 ID 列表,供配置选型。

当前仓库中该页面的后端即 deeptutor/api/routers/settings.pyGET/PUT /network_network_settings_payload() 内部用 normalize_origins() 处理 origin 集合,并区分 explicit / permissive 两种 CORS 模式),而 "fetch models" 对应 POST /fetch-models——它是 deeptutor/services/llm/factory.fetch_models 的薄 HTTP 封装,按 binding + base_url + api_key 请求后返回 model_ids。这与发布说明中"#523(model fetching + notebook lookup)本地重实现后合入"的描述相符。

社区修复与变更全量盘点

v1.4.1 合入的社区贡献与内部修复可从发布说明中完整对齐:

安全闭包(Security)

编号 漏洞类型
#518 经 shell 工具触发的 TutorBot RCE
#517 文件系统工具路径穿越(path traversal)
#516 跨 bot 文件管理鉴权绕过
#515 跨会话 turn 重新生成鉴权绕过
#514 book 确认流程鉴权绕过
#506 ExecTool 经聊天执行 LLM 提供的 shell 命令(首个加固在 PR #507)

其中 #506→#507 与 #518 的修复即上文"沙箱 opt-in"的直接动因;#516/#515/#514 则是"按用户资源隔离"一节所覆盖的鉴权边界。

缺陷修复(Bug fixes)

  • #520:v1.4.0 回归——首轮对话后聊天输入被禁用;
  • #521 / PR #509:长文档导致知识库 embedding 失败(chunking 管线修复);
  • #512 / PR #513:Docker 环境下新用户无法创建个人资料(同时补上空状态页的 profile 按钮);
  • #527 / PR #528:Qwen 系推理模型原生工具调用失败;
  • PR #508:GPT-5 init-wizard 的 token 参数修正。

合并 / 重构的 PR

  • #528 推理模型原生工具调用、#524 超大会话事件截断、#513 空状态 profile 按钮、#509 chunking 管线修复、#508 GPT-5 探测、#507 ExecTool 加固;
  • 社区贡献 #522(zip 上传)#523(模型拉取 + notebook 查询) 未直接合入原始提交,而是在本地重实现后随本版本发布。

升级注意事项与运维建议

  1. Drop-in 升级pip install -U deeptutor;Docker 用户拉取 ghcr.io/hkuds/deeptutor:latest。从 v1.4.0 升级无需手工迁移。
  2. TutorBot shell exec 已默认禁用:这是本版最需要感知的变更。仍在依赖 shell 能力的 bot 须显式设置 allow_shell_exec=true;即便开启,工具访问仍被限制在工作区内,且该开关不可经由 update payload 远程打开,请在服务端配置面操作。
  3. 跨站 HTTPS 鉴权:若部署于跨站(cross-site)HTTPS 环境,需要显式设置 CORS origins,并开启 cookie_secure=true(同站点策略随之变为 none),否则鉴权 cookie 在跨站请求中不会被携带。可直接在新增的 /settings/network 页面完成上述配置并核对 cross_site_cookie_ready 状态。

小结

v1.4.1 是"小版本、大安全"的典型:它以一次默认行为变更(shell exec opt-in)同时关掉了 RCE 与配置翻转两条路径,用 per-user / per-session 隔离补齐了此前逐点修复的鉴权绕过,用"乐观注入 + 失败降级"而非"前置能力预判"的思路根治了多模态丢图,并为外部集成打开了一条带持久上下文的 HTTP/SSE 通道。结合 v1.4.0 发布说明与后续 v1.5.0 等版本的记录,可以清晰看到这套安全基线(沙箱服务化、data/users/<uid> 目录隔离、normalize_origins CORS 语义)如何被后续版本持续继承与扩展——这也是阅读一份补丁版本时最有价值的脉络。

热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.15 K
2.77 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.36 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
929
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.03 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
534
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.47 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
398
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.05 K
529