Codex 沙箱三种模式深度对比——read-only、workspace-write、danger-full-access
Codex 沙箱三种模式深度对比——read-only、workspace-write、danger-full-access
我有个习惯——网上看到有用的代码片段,会随手扔进项目里跑一下。大部分时候没事,但有几次把开发环境的 Python 包搞炸了,排查了半天。
Codex 的沙箱就是为这种场景设计的——在隔离环境里跑代码,搞坏了就搞坏了,确认没问题再落地。
这篇文章不打算把沙箱原理写成内核文档,而是还原三个真实场景:什么时候该用沙箱、怎么用、什么时候不该用。
沙箱到底隔离了什么
先花半分钟搞清楚沙箱做了什么。
Codex 的 OS 级沙箱是直接卡在内核层面的一套访问控制,不是 Docker 容器。macOS 上用 Apple Seatbelt(sandbox-exec),Linux 上用 bubblewrap + seccomp + Landlock。
默认的workspace-write模式:
| 操作 | 允许? |
|---|---|
| 读写当前目录 | 允许 |
| 读写系统临时目录 | 允许 |
| 启动子进程 | 允许(同样受沙箱约束) |
| 访问网络 | 默认不允许 |
写/etc、~/.ssh等系统路径 | 不允许 |
修改.git/目录 | 不允许 |
执行rm -rf / | 三重防护:去 sudo + 路径限制 + 内核 DAC |
三种模式一言蔽之——read-only(看)、workspace-write(改项目)、danger-full-access(什么都行)。
理解了这层,看下面三个场景就知道什么时候用它。
场景一:GitHub 上扒的脚本,敢跑吗
上周在 GitHub 看到一个批量重命名工具——按日期前缀整理散落的照片文件。代码不长,150 行。作者在 README 里写了"use at your own risk"。
我拿到脚本后用 Codex 跑了一下,不碰本地文件。
$cd~/projects/photo-tools $mkdirsandbox-test $cdsandbox-test $echo'photo1.jpg'>test_rename.py# ...把要测试的脚本放进来$ codexexec"这个目录里有一个 rename_photos.py,读它的内容, 然后创建一些测试用的假文件,用它批量重命名。 修改的文件用 diff 展示给我看。"Codex 在沙箱里创造了一个微缩测试环境:
→ Reading rename_photos.py (152 lines) → Tool: shell $ touch IMG_20260415_120000.jpg $ touch IMG_20260416_093000.jpg $ touch IMG_20260417_184500.jpg → Tool: shell $ python rename_photos.py --dry-run . → dry-run results: IMG_20260415_120000.jpg → 2026-04-15_IMG.jpg IMG_20260416_093000.jpg → 2026-04-16_IMG.jpg IMG_20260417_184500.jpg → 2026-04-17_IMG.jpg 3 files would be renamed, 0 conflictsdry-run 正常。然后它发现了问题:
→ Tool: shell $ python rename_photos.py . (without --dry-run) → Uh oh — one file had same name pattern but a different date format, was renamed to empty string. Bug in the REGEX_DATE pattern on line 47. It matches 'IMG_20260415' but the script assumes 'IMG_' always precedes the date.这个 bug 如果我在本地跑,轻则文件名损坏,重则数据丢失。在沙箱里发现了,零代价。我改了正则表达式,在沙箱里重新验证,确认无误后才把脚本放进正式目录。
场景二:数据库迁移,怕把生产数据搞坏
团队的 Flask 后台需要加一个新表、改两个旧字段。迁移脚本我写好了,但 SQLite 的 ALTER TABLE 有限制——它不支持直接修改列类型,常见的 workaround 是建新表→导数据→删旧表→重命名。
每一步都像定时炸弹。
$ codexexec" 1. 创建一个测试 SQLite 数据库 2. 按当前 schema 建表并插入 50 条真实结构的测试数据 3. 运行 migration.sql 4. 验证:新表结构是否正确、数据是否完整、外键是否正常 5. 如果有问题,报告具体哪里出错 "沙箱里的执行过程:
→ Tool: shell $ sqlite3 migration_test.db < 01_create_schema.sql $ python seed_data.py --count 50 --db migration_test.db → 50 records inserted, schema matches production → Tool: shell $ sqlite3 migration_test.db < 02_migration.sql → Error: ALTER TABLE orders ALTER COLUMN status TEXT near "ALTER": SQLite does not support ALTER COLUMN → Analysis: 迁移脚本第 7 行使用了 SQLite 不支持的语法。 Suggested fix: 1. CREATE TABLE orders_new (...) 2. INSERT INTO orders_new SELECT ... FROM orders 3. DROP TABLE orders 4. ALTER TABLE orders_new RENAME TO orders This preserves all data and works with SQLite. → Generating corrected migration: [+] 02_migration_v2.sql (18 lines) → Testing corrected migration on fresh test DB: → Schema verified: 3 tables, 14 columns → Data integrity: foreign keys intact, 50 records → NULL handling: no unexpected NULLs introduced ✓ Migration passed如果这个 SQLite 语法错误在我的开发数据库上直接跑——错误信息不致命(SQLite 只是拒绝执行这条语句),但数据可能处于半迁移状态。在沙箱里发现、分析、修正、再验证,确认后的迁移脚本才应用到实际项目。
场景三:试一个你没用过的库
pip install 一个陌生库,可能会污染环境、装一堆依赖、甚至覆盖已有包版本。出问题之后pip uninstall未必能回到之前的状态。
Codex 沙箱是完美的"试吃"环境。
$ codexexec" 我通常用 matplotlib 画图,听说 plotly 做交互式图表更好。 用我们最近的销售数据(sales_2026_05.csv),分别用 matplotlib 和 plotly 各画一张按月分组的趋势图。 不用生成图片文件——把两个方案的代码、API 差异、 和性能对比列出来就行。 "沙箱里干净安装了 plotly,不会影响我本地已有的 matplotlib 环境:
→ Tool: shell $ pip install plotly pandas Collecting plotly-6.5.0... Successfully installed plotly-6.5.0 pandas-2.3.3 numpy-2.2.6 → Tool: shell $ python plotly_vs_matplotlib.py → Comparison: matplotlib: 12 lines of code, 3.2s render time (static PNG) plotly: 8 lines of code, 0.8s render time (interactive HTML) → Key API differences: - plotly uses figure factory pattern vs matplotlib's procedural style - plotly's px.line(df, x, y) handles grouping automatically - matplotlib requires explicit groupby + subplot generation → Recommendation: plotly for interactive dashboards, matplotlib for print/PDF reports. Both have their place.沙箱跑完,plotly装过、用过、卸载——我本地的pip list完全没变。但我拿到了两个库的代码对比和性能数据。
沙箱不是什么
三次实战用下来,对沙箱的边界有了清晰认识。
沙箱不是 Docker。它不是完整的操作系统虚拟化。你的文件修改发生在工作目录内——不是 copy-on-write 层。沙箱用内核级安全策略限制进程能碰什么、不能碰什么,但它没有独立的文件系统、网络栈或内存空间。
沙箱不能保护你免受自己的错误判断。如果你确认了一个有问题的 patch 让它落地,沙箱不会拦你。它保证 AI 在探索阶段不越界,但最终的决定权在你按Y的时候。
沙箱不适合需要全项目上下文的操作。如果想改一个涉及 10 个文件的功能,沙箱的懒加载策略可能漏掉关键文件。这种场景用 Claude Code 更适合。
什么时候该用哪种模式
日常工作里,三种模式的决策很简单:
只读代码、不执行 → read-only 装包、改项目文件、跑测试 → workspace-write(默认) 需要联网装依赖 → danger-full-accessdanger-full-access听起来吓人,但日常开发中装 npm/pip 包就需要这个权限。用了它不代表不安全——内核沙箱仍然在,只是去掉了网络限制。真正需要警惕的是--yolo,那个跳过所有保护。
实践下来,绝大多数场景用默认的workspace-write就够了。只有在装包或调外网 API 的时候切一下。
下一篇
章的主题顺理成章——让 AI 写测试。一个用 Claude Code 从 0 覆盖到 80% 的实战案例,以及哪些测试不该让 AI 写。
