本版修复两个实测可复现的沙箱逃逸漏洞:Agent 的写入能越出工作区围栏。
两者均在自审中发现,各自带 POC 与有效性自检(临时回退修复 → 断言必须 FAIL),
确认测试真的锁住行为而非恒真。
建议所有在真实主机上跑交互模式的用户升级。SWE-bench 评测在容器内,不受影响。
1. 路径围栏不跟随符号链接
_validate_path 用 os.path.normpath 规范化路径——它只在字符串层面消解
..,不跟随符号链接。工作区内存在指向外部的链接时,路径在字符串上看仍在围栏
内,校验放行,而写入实际落在围栏外。
复现(修复前):
工作区 ws/ 内有 escape -> /home/user
write_file('escape/PWNED.txt') → success: True
文件实际落在 /home/user/PWNED.txt(围栏外)
读路径同样中招,意味着凭证读取防护(禁止读 .ssh/credentials 等)也能被
绕过。这不是理论风险:符号链接在克隆仓库、node_modules、虚拟环境里极其
常见,Agent 自己也能用 command 工具创建链接后再写入。
修复:改用 Path.resolve(strict=False),让围栏比对看到真实目标。12 个
调用点全部经由 _validate_path 这一收敛点。strict=False 是必需的——写入常
指向尚不存在的文件。恶意路径无法关掉围栏:符号链接循环由 pathlib 抛
RuntimeError(不是 OSError),超长路径抛 OSError,两者都退回文本
规范化且结果仍在围栏内。
2. 交互路径未绑定 ToolRuntime,模型自选 cwd 可移动围栏
bind_tool_runtime 此前只在子代理 spawn 与 SWE-bench 评测中调用,
xenon/repl/ 下一处都没有——交互模式始终运行在 ToolExecutor.runtime is None
状态。该状态下 _runtime_params 原样返回模型参数,而:
cwd在ToolNode._VALID_PARAMS内,模型可以提供;- 围栏根由
cwd推导(_get_allowed_root)。
两者相乘 = 模型自选的 cwd 移动围栏本身,而不是被围栏约束。
修复:在 _run_engine(七种范式唯一装配收敛点)绑定 ToolRuntime,可信根
覆盖每次工具调用的 cwd。子代理连带受益:spawn 路径读取 node.tool_runtime,
此前交互模式下它恒为 None。绑定失败只记警告不上抛,任务不因目录被删而整体
不可运行。
一致性
此前交互模式与评测模式跑在两套不同的隔离强度上——40% 的 SWE-bench 成绩是
在强隔离(容器 + 绑定 runtime)下测的,交互模式却没有同等约束。本版消除该差异。
验证
- 新增
tests/test_symlink_fence.py(14 例)+tests/test_interactive_tool_runtime.py(6 例) - 全量 2359 passed, 8 skipped,零回归
- 端到端:
pip install -e .后用真实包 + 真实工作区 + 真实符号链接验证,
逃逸被拦且围栏外零污染,write/read/edit/list正常链路全部通过
升级
pip install -U "git+https://github.com/xianyu-sheng/Xenon.git@v0.8.5"Full Changelog: v0.8.4...v0.8.5