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 開發筆記
更新紀錄: 首次發布