一次 Allow 管不了企业 Agent

发布边界:四层模型是工程控制框架;具体产品的权限、隔离和合规能力必须逐项核验。

摘要

交互式 Ask/Allow 只能表达一次人机确认,不能承担企业安全边界。本文把企业 Coding Agent 的控制拆为 Policy Decision、Scoped Credential、Sandbox Execution 与 Provenance 四层,并说明 MCP、LLM Gateway、网络出口和预批准任务模板应当如何接入同一条不可绕过路径。

关键词

Coding Agent、Policy、Scoped Credential、Sandbox、Provenance

目录

设想一个并不夸张的失败场景。

一名工程师让 Coding Agent 执行仓库里的发布校验脚本:

./ops/reconcile.sh --environment staging

弹窗展示了脚本名、参数和工作目录。命令里没有删除磁盘、上传源码或直连生产库,看起来只是一次常规的预发布检查。工程师点击 Allow。脚本随后读取容器中挂载的默认云凭证;这组凭证属于生产账号。与此同时,脚本使用的内部服务别名因为旧配置解析到了生产控制面。Agent 最终没有破坏本地文件,却向生产环境写入了一条配置变更。

事后能找到的审批记录只有:某个用户在某个时间允许执行这条命令。记录没有回答六个更关键的问题:脚本实际使用了哪个身份,凭证能访问哪些资源,域名最终解析到哪里,网络请求命中了哪个账号,运行时加载的是哪个脚本版本,目标系统确认发生了什么副作用。

这就是 Ask/Allow 模型的根本边界:审批可以证明某人同意过一次调用,但不能独立证明调用被限制在正确的身份、环境、资源和后果之内。

企业 Coding Agent 的安全边界必须贯穿四层:执行前的 Policy Decision、执行时的 Scoped Credential、运行环境中的 Sandbox Execution,以及执行后的 Provenance。四层不是成熟度清单,也不是同一能力的不同名称。它们分别约束“能不能做”“以谁的身份做”“能触达什么”“如何证明做了什么”。缺少任何一层,审批都可能退化成一张只写着“用户点过允许”的收据。

一次误批如何越过四层

审批是决策界面,不是隔离机制

Ask/Allow 弹窗解决的是人机交互问题:Agent 提出一个动作,人决定是否继续。它最适合处理例外、语义不确定和高风险动作的最终确认。但弹窗本身通常看不到完整的有效执行语义。

一条命令的真实风险并不只由字符串决定。它还取决于调用主体、当前目录、符号链接、Shell 解析、环境变量、子进程、配置文件、凭证来源、DNS 与代理、MCP Server、目标资源状态以及脚本在执行时加载的代码。即使弹窗准确展示了原始参数,也未必展示了这些解析后的值。

Anthropic 的 Claude Code 文档本身就区分了权限规则与运行时约束:denyaskallow 按 deny、ask、allow 的顺序求值,宽泛 deny 不会被更具体的 allow 覆盖;规则由客户端执行,而不是由模型或 CLAUDE.md 指令执行。[^S01] 这说明“模型承诺不做”与“系统禁止它做”是两类机制。文档也明确指出,Read/Edit 规则不能阻止任意 Python 或 Node 子进程自行打开文件,若要对所有进程实施路径限制,需要 OS 级 Sandbox。[^S01]

因此,正确的问题不是“审批弹窗够不够严格”,而是“审批之后还有哪些不可绕过的技术边界”。

四层治理必须独立

第一层:Policy Decision 决定动作是否有资格进入执行面

Policy Decision Point,简称 PDP,应该在工具真正运行前,把自然语言意图转成可判定的授权请求。它不只匹配命令前缀,而要接收一组规范化事实:

  • 谁触发任务,代表哪个企业身份;
  • 目标仓库、分支、工作树和数据等级;
  • 工具、脚本、MCP Server 及其版本或摘要;
  • 解析后的文件范围、网络目标和预期副作用;
  • 请求的凭证能力、目标环境和有效期;
  • 当前策略版本、例外编号和审批上下文。

PDP 的输出也不应只是布尔值,而应是一份版本化决策:allowdenyrequire_approval,外加允许的能力集合、决策理由、策略摘要、到期时间和必须满足的运行条件。后续 Credential Broker 与 Sandbox 必须消费这份决策,而不是重新从聊天记录猜测授权范围。

Claude Code 的企业配置提供了一个有用的硬策略范式:managed settings 优先于命令行、本地项目、共享项目和用户配置,并且来自任意作用域的 deny 仍然获胜;组织还可以禁止 bypass 模式、只允许托管 Hook、只允许托管 MCP Server,或把文件和网络白名单收敛到托管配置。[^S01] 这类“用户不能在任务现场自行放宽”的控制,才接近企业 PDP。

PreToolUse Hook 很有价值,但它不是四层治理的替代品。Claude Code 允许 Hook 在工具调用前返回 deny、ask 或 allow,而且 deny 可在旁路权限模式下继续阻断。[^S06] 它适合把工具调用送往企业策略服务,或检查参数、工单、数据分类和目标环境。但官方文档也暴露了其边界:多个匹配 Hook 会并行运行,一个 Hook 的拒绝不会撤销同组其他 Hook 已产生的副作用;某些过滤条件是尽力解析;PostToolUse 发生在动作之后,无法撤销已经完成的外部写入。[^S06] 所以 Hook 应被当成 PDP 的接入点和执行拦截器,而不是天然具备事务性、隔离性和完整证据链的安全系统。

硬策略还必须 fail closed。无法解析的 Shell、未知包装器、动态脚本或无法解析的目标,不应因为“没有命中 deny”而自动进入 allow。安全默认值应是 ask、降权执行或拒绝,而不是把分析器的不确定性转换成权限。

凭证必须短命且窄

第二层:Scoped Credential 限制 Agent 以谁的身份、对什么资源执行

前面的失败场景中,命令本身并不危险,危险来自它拿到了一组错误而过宽的凭证。权限弹窗批准的是“运行脚本”,但云平台最终授权的是“持有这组凭证的主体可以修改生产资源”。两者之间如果没有绑定,审批意图就无法约束实际身份。

企业 Agent 不应长期继承开发者主机上的 ~/.aws~/.ssh、Kubeconfig、浏览器会话或全局 Personal Access Token。更稳妥的模式是:PDP 先批准一份具体能力请求,Credential Broker 再按任务即时签发短期凭证。凭证至少应绑定以下维度:

  • audience:只对指定 API、仓库或控制面有效;
  • resource:只覆盖指定项目、仓库、环境或命名资源;
  • action:只允许读取、创建草稿 PR、写入某个存储前缀等必要动作;
  • subject:绑定 Agent 任务、企业用户和工作负载身份;
  • lifetime:分钟级有效期,任务结束立即失效或撤销;
  • context:绑定分支、变更集、审批 ID、网络出口或运行时证明。

Google Cloud 的 Workload Identity Federation 和 AWS STS 临时凭证说明了这种身份模式的通用基础:外部工作负载可以交换得到短期令牌,而不是分发长期密钥;临时凭证到期后不能继续复用。[^S15][^S16] 这不是某个 Coding Agent 已经完整实现的功能,而是企业设计 Agent 凭证面的可复用模式。

Credential Scope 与 Sandbox Scope 必须同时存在。Sandbox 可以阻止进程读取主机文件,却无法自动把一个已注入的生产管理员令牌变成 staging 只读令牌;反过来,短期凭证可以缩小云端权限,却不能阻止进程读取工作区外的源码或把数据发送到另一个允许访问的端点。身份边界回答“以谁的权限做”,运行时边界回答“从哪里、通过什么路径做”。

Devcontainer 尤其容易制造虚假的安全感。Claude Code 的官方开发容器文档明确提醒:仓库通常以 bind mount 形式进入容器;在旁路权限模式下,恶意仓库仍可能读取容器内可访问的凭证并经允许的网络外传;不应随意挂载主机密钥,而应优先使用仓库级、短期 Token。[^S05] 容器改变了进程所在的位置,却不会自动修复过宽凭证、Docker Socket、宿主机挂载或网络出口。

Sandbox 管后果,不猜意图

第三层:Sandbox Execution 把批准后的最大破坏半径压缩到任务范围

Sandbox 的目标不是判断动作是否合理,而是即使判断失误,也限制进程实际能够触达的文件、网络、进程和生命周期。

工作目录约束、开发容器、虚拟机、网络出口控制和临时凭证经常被混为一谈,实际上它们解决不同问题:

  • 工作目录规则主要约束 Agent 客户端如何使用内置文件工具;
  • OS Sandbox 约束命令及其子进程能访问的文件与网络;
  • Devcontainer 提供可复现环境和一定的命名空间隔离,但安全强度取决于挂载、Capabilities、Socket、用户、内核与网络配置;
  • VM 提供更强的主机隔离单元,但仍可能拥有错误凭证、开放出口或长期磁盘;
  • Egress Policy 决定数据能发送到哪些域名、IP、端口和 HTTP 方法;
  • Credential Scope 决定目标系统最终接受哪些动作。

Claude Code 的普通工作目录模型允许在启动目录及其子目录内写入,读取目录外内容则可通过审批扩展;这不是完整的进程隔离。[^S03] 其 Bash Sandbox 才使用操作系统能力限制每条 Bash 命令及其子进程的文件系统和网络访问。[^S04] OpenAI Codex 的官方文档同样把审批与 Sandbox 分开描述:审批决定何时暂停询问,Sandbox 决定命令能够访问哪些文件和网络。[^S10][^S11] 这些是不同产品各自的实现,但共同证明“决策”与“可达性”必须分层。

企业 Sandbox 至少要明确:只读基础镜像、可写工作区、不可见的主机目录、禁止特权模式、禁止宿主 Docker Socket、受控 DNS、默认拒绝网络、域名与方法白名单、进程和资源配额、固定超时、任务结束销毁,以及对派生子进程同样生效的策略。允许联网时,应通过可记录目的地与请求身份的 Egress Proxy,而不是简单开放整个互联网。

bypassPermissions--dangerously-skip-permissions 的名字已经说明风险。Claude Code 文档要求只在容器或 VM 等隔离环境中使用旁路模式,并允许组织通过 managed settings 禁用它。[^S01][^S02] 这里的“隔离环境”不能只理解为“命令跑在 Docker 里”。只有当文件、网络、凭证、进程权限、生命周期和宿主接口都已收敛时,取消逐次弹窗才有安全依据。旁路审批应该是受控自动化运行时的结果,而不是为了减少点击直接关闭最后一道可见门槛。

GitHub Copilot cloud agent 提供了另一种产品级例子:任务运行在临时 GitHub Actions 环境中,默认防火墙限制互联网访问,并把 Agent Secret 与其他 Actions、Dependabot、Codespaces Secret 区分管理。[^S13] 这些能力属于 GitHub 的云执行面,不能推导为本地 Agent、任意自托管 Runner 或其他产品自动具备同等边界。

第四层:Provenance 证明实际发生了什么,而不是只保存 Agent 说了什么

如果执行后只保留对话、模型回答和“用户已允许”,团队无法重建事故。Provenance 必须连接意图、决策、身份、运行时和目标系统副作用。

一条可重建的执行证据至少应包含:

  • 原始任务、规范化 Task Manifest 与批准范围;
  • PDP 决策、策略版本、规则来源和例外编号;
  • Credential Lease ID、签发者、权限摘要、audience 与到期时间;
  • Sandbox 镜像摘要、运行时策略、工作区 Commit、文件基线与销毁结果;
  • 工具、脚本、Plugin、Skill、MCP Server 的来源、版本、配置与能力摘要;
  • 实际参数、解析后的文件路径、网络目的地、退出码、Diff 和生成物;
  • 目标系统返回的 Request ID、Commit SHA、PR ID、Deployment ID、Cloud Audit Event 或其他效果收据;
  • 任务结束后的凭证撤销、临时资源清理与残留检查。

OpenTelemetry 可以承载其中一部分,但不能被写成天然完整的审计账本。Claude Code 的监控文档说明,Trace 可关联 Prompt、API 与工具执行;同时,用户 Prompt 默认脱敏,Assistant 事件不包含 thinking 和 tool-use block,部分字段会截断,详细工具输入需显式开启,某些提前退出路径可能不产生预期事件,中间重试也不一定逐次形成独立事件。[^S08] 文档还指出事件流本身不负责异常检测、跨源关联和告警。[^S08]

因此,“有日志”只证明某个组件输出过记录,不证明记录覆盖所有副作用,更不证明不可篡改、身份可靠、时钟一致、保留合规或可独立验证。企业需要把 Agent 侧事件与目标系统收据、网络代理日志、凭证签发记录和代码托管记录连接起来,并定义完整性、留存、访问控制和校验策略。真正有价值的 Provenance 能回答:这次变更由谁发起,依据哪版策略,以哪组短期权限,在什么运行时中,通过哪个工具版本,对哪个真实资源产生了什么结果。

企业落地先做三件事

一条可执行的治理路径

四层治理需要形成一条连续执行路径,而不是四套互不相干的后台系统:

用户 / 工单 / CI 触发

Canonical Task Manifest
(主体、仓库、分支、工具、目标、预期副作用)

Policy Decision Point
(deny / require_approval / allow + policy hash)

Credential Broker
(签发 audience、resource、action、TTL 受限的 lease)

Ephemeral Sandbox
(固定镜像、文件边界、默认拒绝网络、资源与时间上限)

Tool / MCP Execution through Egress Proxy
(校验版本、目的地、参数与 capability)

Target-system Receipt
(Commit、PR、Deployment、Cloud Request ID、实际状态)

Provenance Join + Credential Revocation + Sandbox Teardown

这条路径有一个关键性质:上游决策会被下游强制消费。PDP 允许访问 staging 并不意味着 Agent 获得“云平台通用权限”;Credential Broker 只签发 staging 所需能力;Sandbox 只允许访问 staging 端点;Provenance 再验证目标收据确实属于 staging。任何一层发现范围扩张,都应停止执行并重新决策。

控制层与失败模式

控制层它回答的问题可以阻断的失败不能替代的能力缺失时的典型后果
Ask/Allow 界面人是否同意当前展示的动作明显危险或语义不确定的单次调用硬策略、身份收敛、运行隔离、效果证明只留下“用户点过允许”,却无法约束实际目标
Policy Decision这项能力请求是否符合组织规则禁止工具、错误环境、越权参数、未批准例外凭证最小化与 OS 级执行边界个人配置、提示词或临场批准放宽组织要求
Scoped CredentialAgent 以谁的身份、能对哪些资源做什么长期密钥滥用、跨账号、跨环境、权限过宽文件隔离、网络隔离与副作用审计安全命令携带生产管理员身份执行
Sandbox Execution进程和子进程实际能触达什么越界读写、任意联网、宿主接口滥用、任务残留业务授权判断与目标系统权限一次误判扩散为主机、内网或多仓库事故
LLM Gateway模型请求如何认证、计费、限流、审计和路由未授权模型调用、预算失控、端点选择错误本地文件、Shell、MCP 与云资源的 Sandbox模型调用被管住,但本地进程副作用仍不可控
Provenance实际执行链和最终效果能否重建无法归因、无法复盘、只信 Agent 自述执行前策略和执行时隔离日志存在,却无法证明完整性与真实目标状态

MCP 信任必须绑定具体工件,而不是永久信任一个名字

MCP 把 Coding Agent 的能力面从本地命令扩展到数据库、工单、云资源和内部服务。此时“谁信任 Server”必须明确。Claude Code 对项目级 .mcp.json 提供首次批准流程,克隆仓库不能自行完成批准,用户也可以重置项目选择。[^S07] 这说明信任决策属于用户或组织,而不是仓库中的配置文件本身。

但现有文档没有承诺:只要 Server 二进制、镜像、版本、配置、Tool Schema 或能力列表发生变化,历史批准就会在所有场景自动失效。企业设计不能把“曾批准过 server-name”当作永久授权。更稳妥的信任对象应是一个可验证元组:来源、传输方式、不可变版本或摘要、配置指纹、工具能力摘要、认证 audience、Owner 和有效期。任何会改变执行语义的变化都触发 block 或重新确认。这是本文提出的治理要求,不是对某个产品现有自动行为的宣称。

不同产品还可能采用不同交互模型。GitHub 文档说明,管理员或仓库维护者配置 MCP Server 后,cloud agent 可能自主调用其工具,因此官方建议只开放必要且尽量只读的工具。[^S14] 不能把 Claude Code 的项目批准流程、GitHub 的仓库配置模型和其他 Agent 的 MCP 行为拼成一个不存在的统一功能。

LLM Gateway 管的是模型请求面,不是本地执行面

LLM Gateway 可以集中保存模型端凭证、按开发者归因用量、设置预算与限流、审计模型请求并进行供应商路由。[^S09] 这些能力对企业治理很重要,但它们作用于 Agent 到模型之间的请求面。

Gateway 通常看不到本地 Python 进程读了哪个文件、Shell 连接了哪个内网地址、MCP Tool 写了哪条工单、云 API 最终修改了哪个资源。即使 Gateway 完整记录了每次模型调用,也不能因此证明本地工具运行在 Sandbox 中,更不能替代短期工作负载身份和目标系统效果收据。模型面和执行面必须分别治理,再由 Provenance 关联。

预批准任务模板比“永远允许此命令”更可靠

企业确实不能让工程师为每个 git status、测试命令和依赖查询反复点击。审批疲劳会促使用户无差别 Allow,最终削弱而不是增强安全性。解决方法不是扩大永久命令白名单,而是把重复任务变成版本化的预批准 Task Template。

一个“更新依赖并创建草稿 PR”的模板,应同时声明:允许的仓库与分支、可改文件 Glob、固定工具或脚本摘要、允许访问的包仓库域名与 HTTP 方法、只读依赖凭证、最长执行时间、最大文件变更数、禁止触达的目录、预期结果为 Draft PR、必须返回的测试和 PR 收据。模板绑定策略版本与有效期;脚本摘要、MCP 能力、网络目的地、凭证范围或预期副作用发生扩张时,自动进入新的 Policy Decision,而不是沿用旧的“不要再问”。

这样,低风险标准任务可以在四层约束下无弹窗运行,高风险偏差才触发人类审批。团队应监控的也不只是弹窗数量,而是范围扩张率、人工覆盖 deny 的次数、凭证超额权限、Sandbox 逃逸请求、缺失目标收据和无法重建的副作用。

结论

Ask/Allow 必须保留,但它只能承担“是否同意当前决策”的职责。把它当作安全边界,会把语义判断、身份授权、进程隔离和事后证明压缩到一个用户瞬间点击的按钮里,而这些问题本来就不可能由同一机制解决。

可靠的企业 Coding Agent 治理需要四个不可替代的不变量:

Policy Decision 决定是否允许这项能力;Scoped Credential 限制以谁的身份、对什么资源执行;Sandbox Execution 限制进程实际能够触达的文件、网络与生命周期;Provenance 证明最终发生了什么。

审批记录可以证明用户曾经同意。只有四层连续生效,企业才能证明这次同意没有被错误凭证、开放网络、可变工具或不完整日志解释成另一件事。

FAQ

Ask/Allow 是否应该取消?

不应该。它仍是有价值的交互控制,但必须位于四层治理中的 Policy Decision,而不是承担全部安全责任。

沙箱能否替代权限策略?

不能。沙箱限制后果,策略决定是否允许;两者解决的问题不同。

企业最先应该建设哪一层?

建议先定义预批准任务模板与默认拒绝策略,同时把长期凭证替换为任务级短期凭证。

参考资料