背景:Coding Agent 的风险不在”会写代码”,而在”能执行动作”
编码 Agent 与普通代码补全最大的区别,是它不只生成文本。它会读取项目文件、修改代码、运行测试、安装依赖、调用命令行工具,甚至连接外部服务。只要 Agent 获得了 shell、文件系统和网络能力,它就进入了一个更接近”自动化运维脚本”的风险模型。
在这个模型里,问题不只是模型是否聪明,而是它被允许看到什么、修改什么、访问哪里、什么时候必须停下来请求确认。一个提示词写错、依赖脚本异常、测试命令带副作用,或者第三方包在安装阶段执行脚本,都可能让 Agent 做出超出预期的操作。
因此,生产级 Coding Agent 不应只靠”信任模型不会乱来”。更稳妥的设计是把 Agent 放进可审计、可回滚、可限制的沙盒运行时中,让模型能力与系统权限解耦。
核心原理:把 Agent 执行拆成控制平面和执行平面
一个可落地的 Coding Agent 沙盒,通常可以拆成两部分:
控制平面负责模型调用、任务状态、工具路由、权限策略、审批流程、审计日志和恢复逻辑。它决定 Agent 是否可以继续执行,也记录每一步发生了什么。
执行平面负责真正运行命令、读写文件、安装依赖、启动本地服务、暴露预览端口和保存快照。它应该是可替换的,可以是本地 Unix 沙盒、Docker 容器、云端容器、临时 VM 或托管 Sandbox Provider。
OpenAI Agents SDK 的 Sandbox Agents 文档把这种边界描述为 harness 与 compute 的分离:harness 是模型与工具控制层,compute 是读写文件、执行命令、安装依赖、暴露端口和快照状态的沙盒执行层。这个分离的价值在于,认证、计费、审计和人审可以留在可信控制平面,执行环境只拿到任务所需的窄权限。
文件边界:默认只允许工作区写入,密钥文件显式拒绝
文件系统权限是 Coding Agent 沙盒的第一道边界。一个实用的默认策略如下:
| 层级 | 策略 | 示例 |
|---|---|---|
| 1 | 允许读取运行工具所需的最小系统路径 | /usr/bin, /lib |
| 2 | 允许在当前工作区读写代码文件 | :workspace_roots 设为可写 |
| 3 | 将控制目录设为只读或更严格 | .git, .devcontainer, .codex |
| 4 | 显式拒绝读取敏感文件 | **/*.env, **/*secret*, **/*credential* |
| 5 | 禁止通过 ../ 访问兄弟仓库或用户主目录 | 路径穿越拒绝规则 |
OpenAI Codex permissions 文档中的示例正体现了这种模式:可以把 workspace roots 设为可写,同时通过更具体的规则拒绝 **/*.env。文档也说明,更具体的 deny 规则会覆盖更宽泛的 read/write 授权。
一个简化后的权限配置可以这样表达:
default_permissions = "project-edit"
[permissions.project-edit]
description = "Project editing with narrow filesystem and network access."
extends = ":workspace"
[permissions.project-edit.filesystem]
":minimal" = "read"
[permissions.project-edit.filesystem.":workspace_roots"]
"." = "write"
".devcontainer" = "read"
".git" = "read"
"**/*.env" = "deny"
"**/*secret*" = "deny"
"**/*credential*" = "deny"
[permissions.project-edit.network]
enabled = true
[permissions.project-edit.network.domains]
"api.openai.com" = "allow"
"objects.githubusercontent.com" = "allow"
"*.github.com" = "allow"
"tracking.example.com" = "deny"
这里的关键点不是配置语法本身,而是权限建模方式:先给出最小可运行边界,再用 deny 规则切掉敏感区域。不要先开放整个主目录,再靠提示词要求 Agent “不要看敏感文件”。
网络白名单:把”能上网”拆成可解释的域名策略
很多 Coding Agent 任务需要网络:安装依赖、读取包索引、访问 GitHub、调用模型 API、拉取测试 fixture。但是”需要网络”不等于”需要访问任意地址”。
更安全的做法是把网络权限拆成三层:
第一层:默认无网络
本地重构、静态分析、单元测试优先在无网络环境执行。这些任务的输出质量不应依赖网络访问。
第二层:任务级白名单
当任务确实需要联网,只放行明确域名,例如:
- 代码托管:
github.com,gitlab.com,*.github.com - 包管理镜像:
registry.npmjs.org,pypi.org,crates.io - 模型 API:
api.openai.com,api.anthropic.com - 内部服务:
artifacts.internal.example.com
第三层:本地与内网显式审批
以下地址必须作为高风险动作处理:
localhost、127.0.0.1、::1- 私有网段(
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16) - Unix socket(如
/var/run/docker.sock) - 云元数据地址(如
169.254.169.254) - Kubernetes API Server、数据库端口等
特别提醒:对 Coding Agent 来说,
/var/run/docker.sock、本地数据库 socket、Kubernetes kubeconfig 和云 CLI 凭证往往比公网访问更危险——它们是本地逃逸口。
审批流:沙盒边界之外必须停下来
沙盒不是为了让 Agent 永远不能做复杂任务,而是让风险动作显式化。正常编辑代码、运行测试、格式化文件可以自动完成;跨边界动作必须进入审批流。
审批触发条件
| 类别 | 触发条件 |
|---|---|
| 文件敏感 | 读取或修改 .env、密钥、证书、云凭证 |
| 路径越界 | 写入工作区之外的路径 |
| 批量操作 | 删除大量文件或执行不可逆命令 |
| 网络越界 | 访问未在白名单中的网络域名 |
| 高风险工具 | 使用 Docker socket、SSH、云 CLI、数据库客户端 |
| 配置变更 | 修改 CI/CD、部署脚本、权限配置或供应链配置 |
OpenAI Codex sandboxing 文档把 sandbox 和 approvals 区分为两个配合使用的控制:sandbox 定义技术边界,approval policy 决定何时必须停下来询问。这个区分适合直接落到工程设计中:沙盒负责”硬限制”,审批负责”跨边界决策”。
容器不是银弹:Agent 沙盒还要考虑可审计和可恢复
很多团队会把”放进 Docker”当成沙盒的全部。但容器只是执行平面的一个实现,并不自动解决所有问题。
容器可以隔离进程、文件系统视图和依赖环境,但仍要配置用户权限、挂载路径、能力集、seccomp、网络策略、资源限制和密钥注入方式。BPFContain 论文指出,Linux 容器虽然使用 namespace 和资源分区,但隔离语义依赖多种机制拼接,策略容易变得难以理解和审计。这对 Coding Agent 尤其关键,因为 Agent 会动态生成命令,行为边界比普通 Web 服务更难预测。
因此,Coding Agent 的沙盒运行时至少还应提供三类能力:
1. 快照与回滚
每轮任务开始前保存工作区状态,Agent 执行后可以生成 diff,用户确认后再合并。失败时能恢复到任务前状态。
2. 完整审计日志
包括模型请求、工具调用、shell 命令、退出码、文件 diff、网络访问、审批记录和最终结果。审计日志不要只保存在沙盒里,应同步到控制平面,确保沙盒销毁后仍可追溯。
3. 资源限制
限制 CPU、内存、磁盘、进程数、执行时间和输出大小,避免错误命令或恶意脚本导致开发机或共享 runner 不可用。
工程落地:一条可执行的生产链路
一个稳妥的 Coding Agent 执行链路可以按下面顺序设计。
第 1 步:任务进入前做范围声明
用户提交任务时,系统先识别任务范围:
- 目标仓库与允许修改的目录
- 是否允许安装依赖
- 是否允许联网
- 是否允许运行测试
- 是否允许修改配置或部署文件
对于高风险任务(如”修复 CI 并推送""调整 Terraform""清理历史文件""升级所有依赖”),默认进入更严格的 profile,而不是复用普通代码编辑权限。
第 2 步:为任务选择 permission profile
常见 profile 可以分为四类:
| Profile | 适用场景 | 网络 | 文件写入 |
|---|---|---|---|
read-only | 只读分析、代码审查、影响评估 | 关闭 | 禁止 |
workspace-edit-no-net | 普通重构、补测试、文档修改 | 关闭 | 工作区内 |
workspace-edit-net-allowlist | 需访问包源、GitHub 或模型 API | 域名白名单 | 工作区内 |
privileged-manual | 部署、云资源、数据库、跨仓库修改 | 逐步审批 | 逐步审批 |
不要让所有任务都落到
danger-full-access。Codex 文档也明确说明,这类模式会移除文件系统和网络边界,只应在确实需要完整访问时使用。
第 3 步:执行时记录命令与文件 diff
每次 shell 调用都应记录:
- 命令、工作目录、环境变量摘要
- 退出码、stdout/stderr 截断版本、耗时
- 文件修改的完整 diff(不是只记”任务成功”)
如果 Agent 修改了锁文件、生成文件、迁移脚本或 CI 配置,应将其标记为需要人工重点审查。
第 4 步:网络访问进入可观测链路
所有网络请求应至少记录:目标域名、端口、协议、是否命中 allow/deny、请求来源命令和时间。对于包管理器,可以把请求归类到 npm、pip、cargo、maven、apt 等来源,便于后续排查供应链问题。
第 5 步:输出结果前做合并门禁
Agent 完成后不要直接把工作区状态当成最终结果。应先经过合并门禁:
- ✅ diff 是否只在允许目录内
- ✅ 是否包含密钥、token、证书或个人信息
- ✅ 测试是否通过,失败是否有解释
- ✅ 是否新增可疑依赖或 postinstall 脚本
- ✅ 是否修改部署、权限、网络或计费相关配置
- ✅ 是否产生超出任务范围的大规模重构
只有通过门禁后,才允许生成 PR、提交 commit 或交给用户确认。
适用场景
这个方案适合以下场景:
- 团队准备把 Coding Agent 接入真实仓库,不再只是让它生成片段代码。
- 团队希望 Agent 自动修 bug、补测试、做依赖升级或处理 CI 失败,但又不希望它读取密钥或误改生产配置。
- 企业内部存在多个仓库、多个权限等级和多类任务,需要按项目、团队、环境配置不同的 Agent 权限。
- 平台团队正在建设自有 Coding Agent,需要为本地 CLI、Web IDE、云端 runner 和企业审计统一一套运行时边界。
常见误区
误区一:只要模型足够强,就不需要沙盒
模型能力越强,越需要边界。强模型更容易完成复杂操作,也更可能把多个低风险步骤串成高风险结果。权限设计不应依赖模型自我约束。
误区二:容器等于安全
容器只是基础隔离层。没有最小挂载、非 root 用户、网络限制、资源限制、seccomp/capabilities 和审计日志,容器仍可能暴露过大攻击面。
误区三:审批越多越安全
审批过多会导致用户疲劳,最终选择关闭保护。更好的做法是让低风险动作在沙盒内顺畅执行,只在跨边界时触发审批。
误区四:只管 shell,不管文件编辑
很多风险不是通过 shell 命令发生的,而是通过文件修改发生的。例如修改 CI 配置、写入部署脚本、改变依赖版本、生成隐藏配置文件。文件 diff 门禁和路径权限同样重要。
上线检查清单
上线前至少检查以下项目:
- 是否有只读、工作区可写、联网白名单和高权限审批四类 profile
- 是否默认拒绝读取
.env、密钥、证书、云凭证和生产配置 - 是否禁止默认访问用户主目录、兄弟仓库和系统敏感路径
- 是否默认关闭网络,或至少只允许任务所需域名
- 是否对 localhost、私有网段、Docker socket、Kubernetes 配置单独审批
- 是否记录每次命令、退出码、文件 diff、网络访问和审批动作
- 是否能在任务失败或用户拒绝后回滚工作区
- 是否限制执行时间、进程数、磁盘、内存和输出大小
- 是否把部署、权限、CI/CD、依赖升级标记为高风险修改
- 是否有红队用例测试 Agent 读取密钥、跨目录写入、访问内网和删除文件的行为
FAQ
Coding Agent 沙盒是否会降低效率?
会增加少量边界设计成本,但不一定降低日常效率。低风险任务在工作区内自动执行,高风险动作才需要审批。相比每条命令都询问,这种模式更适合长期使用。
网络白名单应该细到什么程度?
生产环境建议至少按任务类型区分:普通代码编辑无网络;依赖安装允许包源和 artifact registry;模型调用允许模型 API;发布相关任务需要单独审批。不要默认放开 *,除非这是明确的临时调试环境。
什么时候可以使用 full access?
只有在任务明确需要完整文件系统或网络访问,并且执行环境是临时、可销毁、无敏感凭证、无生产网络连通的情况下才考虑。对真实开发机和企业仓库,不应把 full access 作为默认模式。