沙箱管理器:把权限档案编译成一行命令
工作区可写,网络收紧。同一条 git status,Mac 套上 sandbox-exec,Linux 套上 helper,Windows 可能原样出门。差别出在编译器。
本页解决的问题
先给结论「沙箱管理器:把权限档案编译成一行命令」要解决的关键问题是什么?
工作区可写,网络收紧。同一条 git status,Mac 套上 sandbox-exec,Linux 套上 helper,Windows 可能原样出门。差别出在编译器。
跟着交接处走,不要只看 Demo。 系统是否可靠,往往取决于模型、工具、状态、权限和人的交接处。把每次交接都当成可以观察、测试和恢复的地方。
为一个自动化步骤写清输入、负责人、审批和恢复动作。
一次运行成功了,却说不清发生了什么,也无法安全重放。
environment_context。
还没渲染。
- 读入 PermissionProfile 三态models.rs L411
- 叠上 additional permissionspolicy_transforms.rs L525
- Auto 则跑 should_require_platform_sandboxpolicy_transforms.rs L541
- select_initial 再问 get_platform_sandboxmanager.rs L62
- None 则原样传出 argvmanager.rs L366
- macOS 前置 /usr/bin/sandbox-execmanager.rs L401
- Linux 序列化档案给 helperlandlock.rs L23
- Windows 第一拍不改 argvmanager.rs L447
- 同一份档案渲染进 environment_contextenvironment_context.rs L96
同一份仓库,三台机器,模型发出同一条 git status。配置里写的是同一份权限档案:工作区可写,网络收紧。
排障的人会以为沙箱没生效。日志里档案还在,模型上下文里那份 environment_context 也还在。只是平台这一层没有把档案编译成包装命令。Windows 上开关关着,编译器交出原 argv,策略层还在。
SandboxManager 先问要不要,再问有没有。should_sandbox 只返回一个布尔。Forbid 恒假,Require 恒真,Auto 看档案形状。有托管网络要求,必须上。网络收紧时,除了调用方自己管文件系统,都要上。网络放开且文件系统不受限,才跳过。
出处:codex-rs/sandboxing/src/manager.rs 第 310 至 329 行 · codex-rs/sandboxing/src/policy_transforms.rs 第 541 至 561 行
然后 get_platform_sandbox 按操作系统给类型。macOS 给 Seatbelt,Linux 给 seccomp helper。Windows 多一个开关,关着就是空。空的意思是这台主机没有可派发的实现。
出处:codex-rs/sandboxing/src/manager.rs 第 62 至 76 行
select_initial 先问前者,再把后者的空收成 SandboxType::None。档案说需要,主机给不出,类型仍是 None。
出处:codex-rs/sandboxing/src/manager.rs 第 293 至 306 行
配置里的意图和主机上的后端,本来就会分手。产品文案、状态栏、模型说明如果都去读配置里希望启用的那一侧,排障会从这里开始歪。两个函数,一个返回布尔,一个返回可选的后端名。换语言重写也用得上。
权限如果写成两份,改了一边忘了另一边,模型就会按旧边界规划下一步。模型看见没有外层沙箱,和 Windows 关沙箱时落到 None,应该是同一份意图的两个出口。
PermissionProfile 三个变体写的是谁负责建外层沙箱。Managed 由 Codex 自己拼包装命令。Disabled 不要外层。External 文件系统由调用方负责,网络仍可能归 Codex 管。
出处:codex-rs/protocol/src/models.rs 第 411 至 425 行
transform 按类型分派。None 原样传出用户 argv,连本机 cwd 都不准备,因为未沙箱的请求可能带着外机路径。macOS 只信任 /usr/bin/sandbox-exec,策略走 -p,路径走 -D,用户命令放在 -- 后面。Linux 把整份档案序列化进 --permission-profile,helper 缺失直接报错,不会按无沙箱降级。Windows 第一拍只校验,argv 不动;包装器就是当前 exe,套壳推迟到 transform_for_direct_spawn。
出处:codex-rs/sandboxing/src/manager.rs 第 365 至 459 行 · codex-rs/sandboxing/src/seatbelt.rs 第 52 至 56 行 · codex-rs/sandboxing/src/landlock.rs 第 15 至 23 行 · codex-rs/sandboxing/src/manager.rs 第 484 至 518 行
同一份档案由 FileSystemContext 收成内部枚举,渲染进 environment_context。Disabled 写成 type="disabled" 加 unrestricted 文件系统。模型可见文本挡不住越权,只减少无效尝试。
出处:codex-rs/core/src/context/environment_context.rs 第 96 至 116 行
运行时事实只应有一份。隔离发生在子进程入口,编排层继续拿路径和档案,到执行边界才落地。平台方言会变,这份同时喂给包装命令和模型的档案形状用得上。
单条命令还可以再带一份 overlay。如果人批的时候用并集,一次误批就能把请求里没有的路径写进会话授权。
合并走并集:任一侧打开网络,结果就是开网;文件系统条目首尾相接去重。求交走另一套:网络必须两侧都开才留下;文件系统只保留落在请求范围内的已批条目。求交结果为空,会话不记账,基座档案继续生效。空集的含义是这次额外开口没留下任何加宽。命令还能不能跑,要看后面的策略和沙箱。
出处:codex-rs/sandboxing/src/policy_transforms.rs 第 90 至 142 行 · codex-rs/sandboxing/src/policy_transforms.rs 第 144 至 214 行 · codex-rs/core/src/session/mod.rs 第 2833 至 2846 行
加宽和批准是两件事。并集让这条命令多开一条路径这件事说得清。求交保证人批下来的不宽过请求。空集表示开口失败,不要解释成改成最严,也不要解释成整条命令禁止。
DeepSeek Harness:缺后端就拒绝执行
DSH 也切在子进程入口。confine 按当前主机选 runner,再把政策编成 runner 参数,用户命令放在 -- 后面。档案词汇只有三个文件模式,danger-full-access 不进包装。
缺后端时 fail closed,不退回原 argv。文案写明 refusing to run the command unconfined。Codex 在 Unix helper 缺失时同样不跑;Windows 关沙箱时把给不出实现收成 None,交给后面的策略层。
Grok:一次 apply,锁住当前进程
Grok 也有一个叫 SandboxManager 的类型,职责是 apply() 再 install()。注释写明:作用对象是当前进程,不可逆。编译产物是 CapabilitySet,不包每条命令的 argv。
平台不支持,或 apply 失败,都打警告、记 apply_failed,然后继续跑。后面的工具调用共享同一份 capability。Grok 不用为每条命令再编 argv,也没法在同一进程里给两条命令两套边界。
出处:packages/sandbox/sandbox-local/src/index.ts 第 316 至 333 行 · crates/codegen/xai-grok-sandbox/src/lib.rs 第 117 至 136 行
四条返回,三组输入
打开 should_require_platform_sandbox,用纸画出四条返回。然后对下面三组给出预测:Disabled 档案加托管网络;根可写加一条 deny、网络 Enabled;External 档案、网络 Restricted。
进阶一问:同一份 Disabled 档案,工具声明 Require,Windows 档位仍关着。类型会是什么,模型看见的 XML 会不会改口。
「先玩一遍 · 同一条命令,三层壳」的能力藏在每次交接里
「同一份仓库,三台机器,模型发出同一条 git status 。配置里写的是同一份权限档案:工作区可写,网络收紧」说明,Agent 的表现不只由模型决定。模型、上下文、工具、状态、权限和人之间的每次交接,都会改变任务能否继续以及出了问题能否恢复。
先写清状态,再增加能力
从「排障的人会以为沙箱没生效。日志里档案还在,模型上下文里那份 environment_context 也还在。只是平台这一层没有把档案编译成包装命令。Windows 上开关关着,编译器交出原 argv,策略层还在」出发,可以把流程拆成起始状态、下一步动作、工具返回、状态更新和停止条件。这样调试时找的是第一处丢失信息或权限的位置,而不是笼统地说“模型变笨了”。
- 读入 PermissionProfile 三态 models.rs L411
- 叠上 additional permissions policy_transforms.rs L525
- Auto 则跑 should_require_platform_sandbox policy_transforms.rs L541
成功路径不能代表系统可靠
用「进阶一问:同一份 Disabled 档案,工具声明 Require ,Windows 档位仍关着。类型会是什么,模型看见的 XML 会不会改口」重放一次成功和一次失败,记录每一轮真正传入的上下文、工具结果和负责人;只要第二个人能复述这条链,系统才有可维护性。
从「先玩一遍 · 同一条命令,三层壳」走到「思路一 · 要不要,和有没有,是两道题」
「先玩一遍 · 同一条命令,三层壳」先把问题落在「同一份档案:看三台机器各自套上什么壳 播放 单步 重置 Windows 档位 关着 RestrictedToken 关着给不出实现。打开之后,Windows 才进入第二拍。 工具偏好 Auto Forbid Require Forbid 钉死不需要。Require 钉死需要,钉不死机器上有没有后端。 托管网络 关 开 打开之后 Auto 必须上沙箱。Windows 若仍关着,类型还是 None。 macOS 待选 g…」上;到了「思路一 · 要不要,和有没有,是两道题」,讨论继续推进到「同一份仓库,三台机器,模型发出同一条 git status 。配置里写的是同一份权限档案:工作区可写,网络收紧」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
分析 Agent 时,沿着状态、动作、工具结果和下一步的顺序走一遍;每次交接都要能说明信息从哪里来、由谁确认、失败时停在哪里。
- 「先玩一遍 · 同一条命令,三层壳」:同一份档案:看三台机器各自套上什么壳 播放 单步 重置 Windows 档位 关着 RestrictedToken 关着给不出实现。打开之后,Windows 才进入第二拍。 工具偏好 Auto Forbid Require Forbid 钉死不需要。Require 钉死需要,钉不死机器上有没有后端。 托管网络 关 开 打开之后 Auto 必须上沙箱。Windows 若仍关着,类型还是 None。 macOS 待选 g…
- 「思路一 · 要不要,和有没有,是两道题」:同一份仓库,三台机器,模型发出同一条 git status 。配置里写的是同一份权限档案:工作区可写,网络收紧
- 「最后的要点」:None 则原样传出 argv manager.rs L366
最后的「最后的要点」把讨论落到「None 则原样传出 argv manager.rs L366」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。