Codex 沙箱三种模式深度对比——read-only、workspace-write、danger-full-access
Codex 沙箱三种模式深度对比——read-only、workspace-write、danger-full-access我有个习惯——网上看到有用的代码片段会随手扔进项目里跑一下。大部分时候没事但有几次把开发环境的 Python 包搞炸了排查了半天。Codex 的沙箱就是为这种场景设计的——在隔离环境里跑代码搞坏了就搞坏了确认没问题再落地。这篇文章不打算把沙箱原理写成内核文档而是还原三个真实场景什么时候该用沙箱、怎么用、什么时候不该用。沙箱到底隔离了什么先花半分钟搞清楚沙箱做了什么。Codex 的 OS 级沙箱是直接卡在内核层面的一套访问控制不是 Docker 容器。macOS 上用 Apple Seatbeltsandbox-execLinux 上用 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 $echophoto1.jpgtest_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 matplotlibs procedural style - plotlys 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 写。