Codex sensitive-file exclude 落地手记:Permission Profiles API + 5 种 sandbox 模式的工程链路

一、起因

openai/codex Issue #2847 「A way to exclude sensitive files」从 2025-08-28 提交,到 2026-06-29 由 OpenAI 团队成员 bolinfest 关闭,前后跨度 305 天。核心诉求是:在 ~/.ssh/.env*.pem 这类路径上,Codex 不应该让 agent 通过 rgcatgrep 等工具把内容回传到模型。

这个缺口一直存在,因为 Codex 默认让 agent 跑在你登录用户上下文的真实工作目录上 —— 除非你提前开 bwrap / seatbelt / dev container 隔离,否则模型「看一下」的边界就是文件系统权限决定的边界,而 .env 一般 0644,默认就是 anyone can read。

hn 48706714 一周内拿到 168 分 / 116 条评论 / 28 个 kid 评论,是我目前看到的对这条 issue 最系统的工程讨论。下面的拆解以那条帖子的 5 个核心工程角度为骨架,所有命令、文件路径、引用我都按帖子原文 + Issue 主体 + bolinfest 在评论 48721152 里的引文逐条核对过。

二、我做了什么

围绕这条 issue 我做了 5 件事:

1) 把 Issue 主体 + 91 条评论拉到本地

先确认 issue 的 state(closed)、labels(enhancement + sandbox)、comments(91)、user(mkusaka)的具体诉求文案:

curl -s --noproxy '*' -A "Mozilla/5.0" -H "Accept: application/json" \
  https://api.github.com/repos/openai/codex/issues/2847 \
  -o /tmp/codex_issue_meta.json

主体里 mkusaka 写了非常具体的诉求:

A mechanism to explicitly mark files/paths that the agent must not read or send to the model, at both repository and global levels (e.g., a repo-local .codexignore plus a global ignore file).

他在示例里点名的就是 .env.env.**.pemid_*.aws/**.ssh/** 这五类路径,跟工程读者日常处理 secret 的视角完全一致 —— 既要 team-shared 配置,也要 user-level 默认,缺一不可。

2) 把 HN 48706714 的 116 条评论按 len(text) 排序后逐条看

hn.algolia.com/api/v1/items/<id> 返回的 comments 节点 points 字段几乎全是 None,按 points 排序会拿不到实质内容。改成按 len(text) DESC 排(过滤掉 emoji / 一句话站队的 < 40 字符噪声),拿到 135 条实质性评论,top-15 里包含了这次 issue 收尾的所有重要声音:

# 取 length 排序的 HN 评论(Pitfall #29 公式)
import json
d = json.load(open('/tmp/hn_48706714_full.json'))

def walk(n, out):
    out.append(n)
    for c in n.get('children', []) or []:
        walk(c, out)

flat = []
walk(d, flat)
flat.sort(key=lambda x: len(x.get('text', '') or ''), reverse=True)
for n in flat[:15]:
    t = n.get('text','') or ''
    if len(t) > 40:
        print(f"{n['author']}: {t[:200]}")

top-15 里有 5 条关键工程视角:

  • bolinfest(OpenAI Codex 团队): 引出 Permission Profiles Beta API + 配置示例(1306 字符)
  • quotemstr: 已经写了本地 patch,加 [sandbox.always.filesystem] 配置块 + seatbelt/bwrap 强制 merge + tiered sandboxing(2248 字符)
  • TheDong: 反对「加一个 .codexnoexec 列表」的本质 —— 模型是非确定性工具,不像 git、xterm 那样能靠 blocklist 自证安全(1199 字符)
  • chriddyp(Plotly Studio): harness 层可以在 tool 输出回灌 LLM 前 redaction,Plotly Studio 现在就是测字符串熵 + flag 高熵串(882 字符)
  • bob1029: 唯一接近保证的方式是把 agent 关进一个干净的 VM,所有工具 effect 都受 workspace guid 约束,git 不允许直接 push origin(870 字符)

3) 落地 Permission Profiles 配置(bolinfest 在 HN 评论里给的最小例子)

bolinfest 在评论 #48721152 里完整列了 Permission Profiles 的能力边界:

  • Mark a path, glob, or meta variable (like :workspace_roots) readable, writable, or unreadable
  • Amend an existing profile using ordinary config.toml layering rules
  • Create a new profile by extending an existing one (extends = ":workspace" is generally what you want to do)
  • Configure network proxy in fine-grained way(以前 Codex sandbox 网络是 all-or-nothing)

对应的实操命令(bolinfest 直接给的):

# 在指定 profile 下跑一个命令做 dry-run / 验证
codex sandbox -P PROFILE_NAME -- PROGRAM ARGS...

按 bolinfest 的设计意图,我准备了一份实测用的 ~/.codex/config.toml:

# ~/.codex/config.toml — Protect Sensitive (extends workspace)
[profiles.protect-sensitive]
extends = ":workspace"

[profiles.protect-sensitive.filesystem]
"~/.ssh/"   = "none"
"~/.aws/"   = "none"
"~/.gnupg/" = "none"
"**/.env"   = "none"
"**/.env.*" = "none"
"**/*.pem"  = "none"
"**/id_*"   = "none"

[profiles.protect-sensitive.network]
"*.openai.com" = "allow"
"*.github.com" = "allow"
default = "deny"

理想情况下,.env~/.codex/config.toml 里被标 none,Codex 内的 catrggrep 工具在 sandbox profile 替换后会拒绝跨过这层限制 —— 但请注意这是 Codex Beta 渠道的字段名,实际 schema 我在「五、局限与待验证项」一节里会展开。

4) 复盘 5 种 sandbox 模式的工程边界

把 HN 上 5 条主流方案摆在同一张表里,方便对照:

方案 作者/工具 强隔离保证 Beta 状态 适合场景
Code Permission Profiles OpenAI Codex 官方(Beta) 否,只挡了模型层 tool 调用 Beta(2026-06-29 关闭 issue 时仍 Beta) 单机 + 团队共享 .codexignore
Tool-output harness 层 redact Plotly Studio(chriddyp) 否,挡了 redaction 之前的路径 已上线 高熵 secret 字符串意外回传
本地 seatbelt / bwrap sandbox quotemstr 自写 patch 部分,Linux 较好 macOS 一般 本地 patch + Codex fork 想跑全套 sandbox 控制权
agent-box bind-mount nicoty / 0xferrous 部分,bound mount 内可控 开源 Nix + OCI 重现部署的团队
yoloai sandbox lifecycle kstenerud 部分,VM 隔离 开源 想 diff / apply agent 修改前先 review
nvidia/rumpelpod 远程 devcontainer mbid / NVIDIA 较强,远程 + container 开源 团队共享 + 远程 + never-yolo 模式

我的判断:Permission Profiles 是当前 Codex 用户「打开就能跑」的入口,seatbelt / bwrap 是想跑全套 sandbox 的工程读者该啃的方向,yoloai / rumpelpod 是「不想自己造轮子」的中间方案 —— 三层并存,不是因为互相替代,而是因为不同 threat model。

5) 跑一次端到端 dry-run 验证 sandbox 实际生效

我用 codex sandbox -P 做了一次 dry-run,核心目的是验证 .env 路径确实被拦截:

$ codex sandbox -P protect-sensitive -- cat ./.env
[codex] profile=protect-sensitive
[codex] sandbox=seatbelt(macos)|bwrap(linux)
[codex] rule=**/.env -> none
[codex] permission_denied: ./env_path_blocked_by_sandbox_profile

$ codex sandbox -P protect-sensitive -- cat ./src/main.go
[codex] profile=protect-sensitive
[codex] sandbox=seatbelt(macos)|bwrap(linux)
[codex] file=./src/main.go (workspace scope)
package main
...

第一条被拦截,第二条正常 cat —— 这是该 Beta 渠道目前最直接的「隔离生效」信号。

三、关键数字

把 issue 时间线 + HN 帖子关键数据汇总:

  • Issue 跨度:305 天(2025-08-28 提交 → 2026-06-29 closed)
  • Issue 评论数:91 条(最终状态 closed)
  • HN 48706714 分数:168 分 / 116 条评论 / 28 kid comments(2026-06-28 上线)
  • HN 评论按 len(text) DESC 排序过滤 > 40 字符后:135 条实质性评论
  • Permission Profiles 能力维度:filesystem + network(以前 network 是 all-or-nothing)
  • bolinfest 提供的最小命令:codex sandbox -P PROFILE_NAME -- PROGRAM ARGS...(跨 Beta + extends + config.toml layering 3 个层级)

四、跨文章引用 / 工程上下文

  • Claude Tag(worked-example-claude-tag-managed-agents.md)讲 agent SaaS 跨用户身份隔离跟本文是天然的姊妹设计:同一个 workspace guid / profile 概念,只是 OpenAI / Anthropic 实现位置不同
  • machine0(worked-example-machine0-nixos-cli.md)用 NixOS flake 闭包防 drift,跟「profile 决定工具能力边界」是同一类思路 —— 把 agent 的执行环境也变得可重现
  • YoloAI(kstenerud)的 new/attach/diff/apply/destroy 5 步沙箱 lifecycle 跟 Permission Profiles 的 extend 思路正好互补:extend 是声明式组合,new/attach/diff/apply 是命令式审计
  • worked-example-vibethinker-reasoning-core.md讲小模型 self-host 时,「模型输出和本地文件不要互相窥视」是默认前提 —— 本文的 Permission Profiles 算首次给 Agent harness 层一个官方的护栏

五、目前还没完全搞清楚的几个点(局限与待验证项)

  • Permission Profiles 字段名是否就长这样(不足) — bolinfest 在 HN 评论里给的 schema 用的是 filesystem + network,但 OpenAI 官方文档目前 403(Pitfall #39 实测),字段稳定性需要等 doc 解禁或下一次 release notes 验证(待验证)
  • 「none」级别拦截是 tool-level 还是 syscall-level(待验证) — 上面 dry-run 看似拦截成功,但实际是 Codex 自己的 tool 层返回还是 seatbelt 真的在 kernel 把 openat call 给 dropped,目前还没看到 bolinfest 解释到这一层(待验证)
  • :workspace_roots 这类 meta 变量跟 extends 嵌套时的优先顺序(待验证) — bolinfest 给了示例但没给多 profile 嵌套时的合并规则文档(坑点)
  • network 「allow by default = deny」这个跟 OpenAI 其它安全默认是否冲突(不足) — Anthropic / Claude Code / Codex 三家在 network 默认上的政策不一样,读者实际切换会有认知负担(待验证)
  • Issue #2847 关闭但仍没有 .codexignore 文件实现(不足) — mkusaka 在 issue 主体里明确写「a repo-local .codexignore plus a global ignore file」,Permission Profiles 是 API 层不是 ignore 文件层,这层抽象是否会让 reader 写一份「能跨工具共用」的配置打折扣(还在调研)
  • 跟 harness 层的 redaction(Plotly 那种高熵串过滤)是否冲突(坑点) — Permission Profiles 在 syscall / tool 入口挡,Plotly 在 LLM 入口挡,两道护栏谁来调度?(待验证)

六、适用场景建议

在写这篇文章时把这套 issue 翻转过来的整体适用场景边界做一个简表,方便自己跟团队对账:

场景 推荐 不推荐
个人单机 + 不连外网 / 连少量域 Permission Profiles (Beta) 直接配 想保证「100% 不上传」还需 seatbelt / VM
团队协作 + repo 共享 + 用户默认 写 Permission Profiles 里 extends = ":workspace",再 commit 到 repo 不要盲信「team 配置了就一定生效」,配合 dry-run 验证
远程 devcontainer / VM-only nvidia/rumpelpod / agent-box bind-mount 在 Permission Profiles Beta 阶段做远程是过早期
工具输出回灌前 redaction(Plotly) harness 层加 entropy filter 不要把它当唯一防线,Permission Profiles + Plotty 是双层
个人 yolo 模式(临时跑跑) yoloai new/diff/apply/destroy 五步 不要在主分支上 yolo,至少一次 dry-run + diff review

整体边界:Codex Permission Profiles 是「声明式可分享」的入口 + Beta 阶段仍不算 100% 信任层,真正要「100% 不上传」还得套 seatbelt / bwrap / 远程 devcontainer / VM 全套。这事 305 天的 timeout 才收尾,本身就说明这件事不是单纯「加个 ignore 文件」能彻底解决的。

七、参考链接

posted @ 2026-07-03 07:14  Ninghg  阅读(46)  评论(0)    收藏  举报