AI Coding Agent 为什么需要权限控制?从 JSON Tool Call 到 Shell、Git、文件系统权限的完整安全模型
安全篇写过攻击面。本篇只写权限模型:工具调用是一张 JSON,执行权在宿主。Shell、Git、文件系统是三道门。点「允许」不等于沙箱已经关上。
阅读前速览
| 门 | 锁什么 | 谁执行 |
|---|---|---|
| Shell | 命令字符串、复合命令 | allow / ask / deny;沙箱另算 |
| Git | status、diff、commit、push;hooks / config | 工作区可写 ≠ 仓库元数据可写 |
| 文件系统 | src、.env、工作区外路径 | 工具规则 + OS 沙箱 |
| 网络 | 域名、出站 | 默认关;单独开名单 |
策略在宿主里评。模型填 JSON,不改门。
权限控制,是因为执行权不在模型手里
Coding Agent 调工具时,模型只产出一段 JSON:工具名加参数。真正跑 git status、改 src/orders/A-1042.json 的是宿主。Schema 管形状。权限策略管「过了形状,还准不准跑」。没有第二道门,第一道门只是礼貌。
- Schema:锁参数。命令必须是字符串,路径必须落在约定前缀。
- 权限策略:allow / ask / deny。先 deny,再 ask,再 allow。官方顺序如此,窄 allow 救不了宽 deny。
- 沙箱:OS 再挡一层。提示注入绕过模型判断时,进程仍出不去工作区。点批准 ≠ 沙箱已开。
工具 JSON 怎么填,见 AI Coding Agent 与 JSON 工具调用。攻击面怎么收,见 Agent 安全危机与 JSON 验证。Schema 为什么是门,见 AI 为什么需要 JSON Schema。本篇不再复述那三条,只写 Shell / Git / 文件怎么落成对象。
权限对象:allow、ask、deny
Claude Code 把规则写进 settings.json 的 permissions。规则由宿主评,不是模型评。CLAUDE.md 只能改它想做什么,不能改宿主允不允许。虚构仓库,不要贴真实密钥:
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff *)",
"Bash(npm test *)",
"Read(./src/**)",
"Edit(./src/**)"
],
"ask": [
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)",
"Read(./.env)",
"Edit(./.git/hooks/**)",
"Edit(./.git/config)"
]
}
}
光杆工具名(如 Bash)作 deny,会把工具从上下文摘掉。带括号的规则挡住匹配调用,工具仍在。复合命令按子命令分别匹配:允许 git status,不等于允许它后面再接别的命令。Codex 用权限档案(:workspace / :read-only),不要和旧的 sandbox_mode 混写。Cursor 用 permissions.json 的前缀名单,再加 sandbox.json。
第一道门:Shell
模型交出的是命令字符串。宿主先对策略,再决定跑不跑。只读查询通常过门:
{
"type": "tool_use",
"id": "call_7f2a",
"name": "Bash",
"input": {
"command": "git status"
}
}
同一套策略里,推远程被 deny。对象形状合法,执行权仍在宿主。Cursor 的终端名单按前缀匹配:git 能罩住 git status。官方也写明,Run Mode 是尽力护栏,不是硬边界。要硬边界,开沙箱。
{
"type": "tool_use",
"id": "call_8a01",
"name": "Bash",
"input": {
"command": "git push origin main"
}
}
第二道门:Git 不是普通写盘
工作区可写,不等于 .git 可写。Codex 在可写根下把 .git、.agents、.codex 默认收成只读;.git 若是 gitdir: 指针,解析后的目录同样保护。Claude Code 沙箱给 worktree 放行共享 .git 的提交所需写入,但 hooks/ 和 config 仍拒绝。Cursor 同样保护 .git/hooks 和 .git/config。推远程这一跳,宿主应当留下对象:
{
"tool": "Bash",
"command": "git push origin main",
"eval_order": ["deny", "ask", "allow"],
"matched": "Bash(git push *)",
"rule": "deny",
"sandbox": "workspace-write",
"execute": false
}
失败就回 tool_result,写明命中哪条规则。不要静默改远程。commit 用 ask:人看见 diff 再过。push 用 deny,除非这条会话明确要发布。
{
"type": "tool_result",
"tool_use_id": "call_8a01",
"is_error": true,
"content": "denied by policy: Bash(git push *)"
}
第三道门:文件系统要两层
一层是工具规则。一层是 OS 沙箱。官方把两层分开写,是因为它们挡的不是同一种调用:
- 工具规则:Claude 的 Read / Edit deny 管内置文件工具,以及它认出来的
cat/head一类命令。任意语言脚本自己打开文件,这层罩不住。 - OS 沙箱:进程级边界。默认只写当前工作区和会话临时目录。工作区外要另开路径。网络默认关。
- 凭证文件:
.env进 deny。不要只靠「别读密钥」这句提示。
MCP 工具是另一张表,每个工具仍要单独过门。协议对比见 MCP Tool Schema vs Function Calling。本篇不写怎么调 MCP,只要求:接进来的工具,权限对象里要有名字。
五步把权限收成对象
不要从「请小心执行」开始。按门写:
- 先写 deny。push、hooks、config、.env、工作区外路径一次列清。
- 只读命令进 allow。status、diff、test。复合命令按子命令各写一条。
- 写盘和提交进 ask。人看见路径和 diff。会话结束,编辑批准不自动变成永久规则。
- 打开 OS 沙箱。Codex 用
:workspace,不要随手:danger-full-access。Cursor 不要用 Run Everything 当默认。Claude 的bypassPermissions只留在隔离容器。 - 策略本身当 JSON 审。allow 和 deny 摊开对比。规则文件进版本库,本机覆盖进 local,不要混成一份说不清的名单。
Schema 怎么当门,见 Agents API + JSON Schema。本篇只要求:Coding Agent 上,每道门都有对象,而且由宿主评。
复盘权限,先摊开两段 JSON
一次越门,多半是规则没对齐,不是模型突然变野。
- 把策略和那次
tool_use格式化,看命中的是 deny 还是漏网的 allow。 - 用 JSONPath 提取 抽出
$.permissions.deny、$.input.command。 - 一次过门和一次被拒 JSON Diff。先改规则,再改提示。
- Git 元数据规则和 src 写盘规则分开审。能改
A-1042.json,不表示能改 hooks。 - 策略文件、仓库路径、会话日志不要贴进公共格式化站。JSONSort 在浏览器本地跑。
两段轨迹都能按规则重建,你才是在收权限,不是在收「我点过允许」。
常见误区
- 以为 Schema 就是权限。Schema 管形状。合法的
git push仍然要被 deny。 - 只写 allow,不写 deny。官方顺序是 deny 先评。没有 deny,名单只是加速点批准。
- 把工作区可写当成 Git 可写。hooks 和 config 是另一道门。官方默认保护它们。
- 只用工具 deny,不开沙箱。脚本自己打开文件,工具规则罩不住。要进程边界。
- 在笔记本上开 bypass / full-access / Run Everything。官方留给隔离环境。日常会话用 workspace + ask。
- Codex 新旧两套一起写。出现
sandbox_mode,权限档案不生效。选一套。
FAQ
点「允许且不再询问」等于永久授权吗?
不一定。Claude Code 里,Bash 的永久批准写进仓库根的 settings.local.json;文件编辑的批准只活到会话结束。Cursor 的名单在 permissions.json。先看写下的对象,不要看按钮文案。
沙箱开了,还要 deny 吗?
要。沙箱挡进程摸到工作区外。deny 挡工作区里不该跑的命令,比如 push。两层锁的不是同一扇门。
Git 为什么单独列?
因为仓库元数据不是普通文件。能改 src,不该能改 hooks 和 config。push 是出站,不是写盘。官方把这几条从「工作区可写」里摘出来。
只读模式够不够?
够用来看代码。不够用来改 A-1042。Codex 的 :read-only、Claude 的 plan、Cursor 的只读沙箱,是探路档,不是发布档。
策略能进 Git 吗?
能,而且应该。组织共用的 deny / allow 进版本库。本机放宽进 local,不要提交。托管设置用来关掉 bypass 和 auto。
结论
Coding Agent 需要权限控制,因为模型只填 JSON。Shell、Git、文件系统是三道门。Schema 过了,宿主还要再评一遍。
先写清 git push 会撞上哪一条 deny。能指到路径,权限才算可验证。模型会换。门应该先稳定。
延伸阅读
- AI Coding Agent 如何用 JSON 调工具 Claude Code、GPT Agent 的工具 JSON
- Agent 安全危机与 JSON 验证 攻击面怎么收,不写权限对象
- Claude 安全事件与工具 JSON 一次事件里,工具参数怎么走偏
- Agents API + JSON Schema 实战 稳定、可验证、可执行的三道门
- 为什么 AI 需要 JSON Schema Schema / Function Calling / Structured Output
- JSONSort 本地工具箱 格式化、校验、Diff
- 全部文章 更多 JSON 与 AI 开发笔记
更新记录: 首次发布