Codex

Windows 下 Codex 全权限误删整盘:成因与防护清单

编辑部阅读约 6 分钟

一位开发者给 Codex 开启完全放行权限后无人值守运行,回来发现 C 盘和 D 盘被清空。事故由四个环节叠加而成:Windows 跨层传参丢失引号、WSL 自动挂载 Windows 盘符、find -delete 无刹车、full access 无人拦截。文章拆解每一环的成因,给出隔离、权限收紧、删除刹车、备份四层可落地的防护方案与检查清单。

事故经过:一条清理临时目录的命令

一位开发者为了在额度重置前把用量跑满,出门前给编程 Agent 下了一条指令:自己去验证整个项目的脚本和文档是否正确,并开启了完全放行模式。几小时后回来,C 盘和 D 盘已被清空,涉及两三百 GB、上百万个文件,只有少数被进程占用的文件留了下来,最后靠云备份恢复。

事后翻执行记录,Agent 本来只是想下载一个 sshpass 二进制到临时目录,用完再清理:

wsl.exe bash -lc 'set -e; d=$(mktemp -d); cd "$d";
  apt-get download sshpass >/dev/null;
  dpkg-deb -x sshpass_*.deb extracted;
  cp extracted/usr/bin/sshpass /tmp/xxx-semantic-bin/sshpass;
  chmod 700 /tmp/xxx-semantic-bin/sshpass;
  cd /; find "$d" -depth -delete;
  /tmp/xxx-semantic-bin/sshpass -V'

出事的是 cd /; find "$d" -depth -delete。$d 本该展开成 /tmp/tmp.XXXXXX,但在操作系统层面真实拿到的命令行里,$d 两侧的双引号已经不见了。引号一丢,搜索起点不再是临时目录而是根目录;而 WSL 默认把 Windows 固定盘挂在 /mnt/c、/mnt/d,find 又不带 -xdev,遍历就跨进了宿主机的两块盘。

四层防护为什么同时失效

1. 跨层传参把引号吃掉了

Windows 的命令行参数在系统层面只是一个字符串,不是字符串数组。一条命令从 PowerShell 传到原生可执行文件,再传给 wsl.exe,最后交给 bash,每一层都要重新解析引号和转义。层数越多,语义出现偏差的机会越大——"在 PowerShell 里写着没问题"的命令,执行时可能完全是另一回事。记录分析指向 PowerShell 调用原生 exe 时没有转义内层引号,但换一种组合,丢失点可能出现在别处,具体在哪儿丢的,得按自己的环境复现确认。

2. WSL 的自动挂载扩大了打击面

一旦工作目录到了 /,且 find 不加 -xdev(或 -mount),遍历就会从 Linux 根文件系统跨到 NTFS 挂载点上。原本"只删临时目录"的操作,作用范围变成了整机。

3. find -delete 没有任何刹车

-delete 是立即生效的删除:不进回收站、不产出中间清单、不打印将删内容。这类命令本该先跑一遍 -print 确认清单,或加 -maxdepth 限制范围。Agent 没做,规则文件里也没禁止。

4. full access 拆掉了最后一道人工闸门

无人值守 + 完全放行 + 高危命令,三件事凑齐后没有任何环节能拦住它。这一条最容易改,也最应该改。

可以落地的四层防护

第一层:把 Agent 关进隔离环境

方案 隔离强度 适用场景 代价
本地虚拟机 + 快照 高 长时间无人值守、需要完整桌面环境 占资源,大项目编译慢
容器 / microVM 沙箱 中高 单一语言项目、依赖可控 需要学习配置,图形化工具有限
独立机器(云主机等) 高 需要长期在线、本机不能停 有额外成本,要同步代码
仅靠审批模式 低 有人盯着、任务较短 长任务频繁被打断

核心原则是只让项目目录进入 Agent 的视野,其他磁盘不挂载或只读挂载。如果任务本身就要长时间在线跑、又不愿让本机磁盘承担风险,可以把它放到一台与本地数据物理隔离的远程机器上,例如整机放在机房的独享 Mac,按月租用、远程连接,代码同步过去,本地磁盘根本不在它的可见范围内。是否走这条路,取决于任务时长和对本机环境的依赖程度。

第二层:权限默认收紧

  • 日常用"替我审批"一类的中间权限,不要默认完全放行。
  • 只有"无人值守 + 已隔离"同时成立,才考虑完全放行。
  • 在项目根目录放一份规则文件,把边界写死,例如:
## 执行边界
- 所有读写必须落在项目根目录内;执行前先打印解析后的绝对路径。
- 禁止 find -delete、rm -rf、xargs rm、git clean -fdx 等批量删除。
- 禁止用 wsl.exe / bash -lc 包裹命令,统一走 PowerShell 7 或 Git Bash。
- 需要清理时,把待删清单写入文件,交人工确认后再执行。
- 删除一律走回收站或移动到 .trash/。

规则文件不是硬约束,Agent 有可能绕开,但它能明显降低"它自己决定顺手删点东西"的概率。

第三层:给删除动作加物理刹车

  • 用 trash 一类的命令替换 rm。注意这层包装通常写在 .zshrc / .bashrc,而 Agent 调起的是非交互 shell,默认不加载这些文件,需要改到 ~/.profile 或系统级配置里,或者直接替换二进制。这一点必须实测,别以为加了别名就安全。
  • 部分 CLI 支持在命令执行前挂 hook,可拦下高危命令并要求确认,具体配置方式以官方文档为准。
  • 执行 find 时强制带 -xdev,并把起点限定在项目目录:
find "$PROJECT_DIR" -xdev -depth -name '*.tmp' -print   # 先看清单
find "$PROJECT_DIR" -xdev -depth -name '*.tmp' -delete  # 再执行删除
  • 如果必须用 WSL,可以考虑关掉自动挂载,让它看不到 Windows 盘符:
# /etc/wsl.conf
[automount]
enabled = false

关掉后 Windows 文件就访问不到了,值不值得看日常习惯;也可以保留挂载,改用 -xdev 加规则文件限制遍历范围。WSL 配置项随版本变化,动手前请核对官方文档。

  • 在 Windows 上尽量让 Agent 固定用一种终端(PowerShell 7 原生或 Git Bash),不要让它自由地在 PowerShell 和 WSL 之间来回切换。

第四层:备份是唯一的后悔药

  • 遵循 3-2-1:三份副本、两种介质、一份异地。
  • Windows 开卷影副本或文件历史记录,macOS 用 Time Machine,Linux 用快照加异地同步。
  • 定期做一次恢复演练。备份没验证过,等于没有备份。
  • 让 Agent 自己"先备份再操作"意义有限——它如果判断得准哪些该备份,多半也不会删错。

几个有分歧的点

是 PowerShell 的锅吗? 一部分人把矛头指向 PowerShell 的引号与转义规则,另一部分人认为换成 bash 一样会出事,真正的问题是 find -delete、WSL 跨挂载点遍历和完全放行三者叠加。更准确的说法是:Windows 的参数拼接方式放大了风险,但它只是链条上的一环,换终端能降低概率,不能消除风险。

换个模型是不是就好了? 有人建议换另一家的 Agent,但多个回复都提到不同工具都出现过删文件的案例。工具差异确实存在,可把安全寄托在"这个模型更聪明"上并不可靠。

人工审批够不够? 有人实测长任务里半小时要确认几十条命令,命令又长又难读;加上引号可能在到达执行层之前就已经变形,人工确认的价值被大幅稀释。审批适合有人盯着、任务较短的场景,长时间无人值守还是要靠隔离。

虚拟机是不是万能? 大型项目的编译、图形化工具、部分驱动在虚拟机里体验不佳,快照也有存储成本。折中做法是快照 + 云备份 + 规则文件三件套,按项目重要性分配投入。

一份可以直接照做的检查清单

  • Agent 的默认权限不是完全放行
  • 无人值守任务只在虚拟机 / 容器 / 独立机器里跑
  • 隔离环境只挂载项目目录,敏感盘不挂载或只读
  • 项目根目录有规则文件,明确禁止批量删除和跨层包裹命令
  • rm 已替换为回收站方案,并验证过在非交互 shell 下生效
  • find 一律带 -xdev 和明确的起始路径
  • 系统级快照 / 卷影副本 / Time Machine 已开启并验证可恢复
  • 关键数据有异地或云端副本
  • 长任务开始前先小范围试跑,检查执行日志

小结

这次事故的可怕之处不在于 Agent 有多"坏",而在于它真心想做好一件事:建个临时目录,用完清理掉。一个引号在多层传参中丢失,加上 WSL 自动挂载和 find -delete 的即时生效,就足以让"清理临时目录"变成"清空两块盘"。关进隔离环境、把权限收紧到刚好够用、给删除动作加一道物理刹车、再准备一份验证过的备份——这四件事没有一件需要高深技术,但缺任何一件,剩下三件都可能白做。

CodexWSLAI Agent 安全文件误删备份隔离