Zed Agent 工具权限完全指南:用 agent.tool_permissions 精细控制 Agent 工具执行
【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed
本文基于 Zed 官方文档 tool-permissions.md 与仓库源码,系统讲解 Zed Agent Panel 中agent.tool_permissions配置项的完整用法:如何为 terminal、edit_file、fetch、MCP 工具等设置自动批准、自动拒绝与强制确认规则,并结合 权限决策引擎 的源码剖析规则优先级、shell 链式命令解析、内置安全规则等底层机制,帮助你把 Agent 的自动化程度调到“既省心又安全”的平衡点。
版本说明:从布尔开关到规则引擎
在 Zed v0.224.0 及以上版本中,工具审批由agent.tool_permissions.default及其嵌套规则控制。更早的版本中,该行为由布尔值agent.always_allow_tool_actions(默认false)控制。本文所有内容以当前仓库(即 v0.224.0 之后)的实现为准。
相关背景可参阅 Agent Panel 文档 与 工具列表文档。
快速开始
可以用 Zed 的 Settings Editor 配置工具权限(agent.tool_permissions设置页),也可以直接编辑设置文件。下面的示例让cargo与npm命令自动放行,而sudo命令逐次确认:
{ "agent": { "tool_permissions": { "default": "allow", "tools": { "terminal": { "default": "confirm", "always_allow": [ { "pattern": "^cargo\\s+(build|test|check)" }, { "pattern": "^npm\\s+(install|test|run)" } ], "always_confirm": [{ "pattern": "sudo\\s+/" }] } } } } }该示例的效果:
terminal工具内的cargo/npm命令自动批准;sudo命令无论全局default如何都会逐次弹出确认;- 非 terminal 类工具遵循全局
"default": "allow",但工具级default与always_confirm规则仍然可以触发提示。
工作原理
tool_permissions通过正则模式对每次工具调用的输入进行匹配,实现三类语义:
- 自动批准(
always_allow):放行你信任的操作; - 自动拒绝(
always_deny):直接拦截危险操作——即使tool_permissions.default设为"allow"也会被阻止; - 强制确认(
always_confirm):无论其他设置如何,命中即弹确认。
支持的工具与匹配对象
不同工具的权限规则作用于不同的“输入文本”,这是写模式前必须理解的一点:
| 工具 | 正则匹配的对象 |
|---|---|
terminal | Shell 命令字符串 |
edit_file | 文件路径 |
write_file | 文件路径 |
delete_path | 被删除的路径 |
move_path | 源路径与目标路径 |
copy_path | 源路径与目标路径 |
create_directory | 目录路径 |
fetch | URL |
search_web | 搜索查询词 |
skill | 该 skill 的SKILL.md文件的绝对路径 |
两个特例:
- MCP 工具使用
mcp:<server>:<tool_name>格式作为工具名。例如github服务器上的create_issue工具写作mcp:github:create_issue。 - Skills:模型自主调用的 skill 走
skill工具权限;而你通过/skill-name斜杠命令手动触发的 skill 不会再次弹确认(因为是你显式调用的)。
完整配置参考
tool_permissions的完整结构如下(字段定义见 settings 内容类型):
{ "agent": { "tool_permissions": { "default": "confirm", "tools": { "<tool_name>": { "default": "confirm", "always_allow": [{ "pattern": "...", "case_sensitive": false }], "always_deny": [{ "pattern": "...", "case_sensitive": false }], "always_confirm": [{ "pattern": "...", "case_sensitive": false }] } } } } }字段语义
| 字段 | 说明 |
|---|---|
default | 所有模式都不命中时的兜底模式:"confirm"(默认)、"allow"或"deny" |
always_allow | 命中即自动批准的规则(除非同时命中 deny 或 confirm) |
always_deny | 命中即立即拦截——最高优先级,不可被覆盖 |
always_confirm | 命中即总是弹确认,即使全局default是"allow" |
工具级的default未设置时,继承全局tool_permissions.default。
模式语法
{ "agent": { "tool_permissions": { "tools": { "edit_file": { "always_allow": [ { "pattern": "your-regex-here", "case_sensitive": false } ] } } } } }模式的两个关键属性,均可从源码确认:
- Rust regex 语法:模式经
regex::RegexBuilder编译,实现在 CompiledRegex; - 默认不区分大小写:
case_sensitive默认false,即case_insensitive(!case_sensitive)(源码位置)。单元测试case_insensitive_by_default验证了CARGO TEST可命中cargo模式(测试位置)。
另外注意 settings 层注释:always_allow/always_deny/always_confirm是ExtendingVec类型,规则会跨设置层(user、project、profile)累积,高优先级层只能追加、不能移除低层规则——在 monorepo 中用项目级设置补充 deny 规则时这一点尤其重要。
无效正则的“熔断”行为
从源码可以看到一个防御性设计:若某工具下有任何一条模式编译失败,该工具的所有调用都会被直接拒绝,而不是静默跳过坏规则。见 check_invalid_patterns:错误信息会列出失效模式数量,提示你去修正tool_permissions。对应测试invalid_pattern_blocks(位置)验证了这一点。
规则优先级:六级裁决的真实执行顺序
文档声明的优先级(从高到低):
- 内置安全规则:硬编码保护(如
rm -rf /),任何设置都无法覆盖; always_deny:拦截命中动作;always_confirm:要求确认;always_allow:自动批准;- 工具级
default(如tools.terminal.default); - 全局
default(tool_permissions.default)。
这与 ToolPermissionDecision::from_input 的实现完全对应,其执行流程可以概括为:
- 先查内置安全规则(仅 terminal 工具有),命中即
Deny,消息固定为 "Blocked by built-in security rule..."; - 检查无效正则,存在即
Deny; - terminal 特判:若命令包含
$VAR、$(...)、反引号等 shell 替换/插值,且该工具不是“无条件全放行”状态(无 deny/confirm 规则且有效 default 为 allow),则直接Deny,因为带变量展开的命令无法静态校验(源码位置); - 进入 check_commands 单遍评估,其逻辑是:
- DENY 短路:任意一条子命令命中 deny 模式立即返回
Deny; - CONFIRM 累积:任意子命令命中 confirm 模式则标记待确认;
- ALLOW 需全中:所有子命令都必须至少命中一条 allow 模式才返回
Allow; - 否则落到
rules.default.unwrap_or(global_default)。
- DENY 短路:任意一条子命令命中 deny 模式立即返回
check_commands的这个“deny 任一命中即拦截、allow 必须全部命中”的不对称设计,是防止链式命令(ls && rm -rf /)绕过白名单的核心,仓库中有专门的注入防护测试族验证(shell 注入测试:&&、;、|、||、反引号、$(...)、后台&、换行符等各种串联方式都不会让^ls白名单误放行)。
Terminal 工具深度解析:Shell 兼容性与解析策略
文档的 Shell Compatibility 一节说明:对terminal工具,Zed 会解析链式命令(如echo hello && rm file),把每个子命令分别拿去匹配你的模式;支持的 shell 包括 sh、bash、zsh、dash、fish、PowerShell 7+、pwsh、cmd、xonsh、csh、tcsh、Nushell、Elvish 与 rc(Plan 9)。
源码里(from_input 的 terminal 分支)还有几个值得注意的细节:
- 解析失败 = 禁用 allow:若命令无法被解析(brush-parser),
allow_enabled置为false,即该命令的always_allow不生效,回落到 default/确认;测试parse_failure_is_denied("ls &&"这类残缺命令)验证了这一点; - 重定向智能处理:
2>/dev/null这类安全重定向在提取子命令时被跳过,不会妨碍白名单命中(dev_null_redirect_does_not_cause_false_negative 测试),而echo hello > /etc/passwd这样的真实文件重定向仍会阻止自动放行; - 管道命令逐段校验:
echo "y\ny" | git add -p file拆成两个子命令,两边都命中 allow 模式才自动批准; - 环境前缀命令:
PAGER=blah git log这类写法需要模式显式匹配前缀(^PAGER=blah\s+git\s+log(\s|$)),旧的^git\b模式不再命中(对应测试)。
内置安全规则:不可覆盖的底线
Zed 内置了一小组任何设置都无法覆盖的硬编码安全规则,只作用于 terminal 工具,用于阻止对关键目录的递归删除:
rm -rf /与rm -rf /*—— 文件系统根;rm -rf ~与rm -rf ~/*—— 主目录;rm -rf $HOME/rm -rf ${HOME}(含$HOME/*)—— 环境变量形式的主目录;rm -rf .与rm -rf ./*—— 当前目录;rm -rf ..与rm -rf ../*—— 父目录。
这些规则会捕获任意 flag 组合(-fr、-rfv、-r -f、--recursive --force),且大小写不敏感;同时对原始命令与链式命令中解析出的每个子命令做检查(如ls && rm -rf /)。除此之外没有其他内置规则。
从源码 HARDCODED_SECURITY_RULES 可以看到其实现比文档描述更“抗绕过”:
- flag 正则同时覆盖短 flag(
-[a-zA-Z]+)、长 flag 及--flag=value形式,且通过TRAILING_FLAGS允许 GNU rm 的“flag 放在路径之后”写法(rm / -rf); - matches_hardcoded_patterns 对
rm命令做多路径展开 + 路径归一化:rm -rf /tmp /(多路径中含危险项)与rm -rf /tmp/../../(路径穿越)都会被归一化后命中; - 测试
hardcoded_cannot_be_bypassed_by_global与hardcoded_cannot_be_bypassed_by_allow_pattern(位置)验证了:即使全局default: allow或配置了always_allow: [".*"],rm -rf /依然被拒; - 安全路径不受误伤:
rm -rf ./build、rm -rf ~/Documents、rm -rf /tmp/test在全放行模式下可正常通过(hardcoded_allows_safe_rm)。
默认设置文件 default.json 中的tool_permissions段落包含了注释掉的示例(保护.env、secrets 目录、私钥等),可以按团队需要取消注释或改写。
全局自动批准
若希望所有工具动作自动通过:
{ "agent": { "tool_permissions": { "default": "allow" } } }这会绕过多数工具的确认弹窗,但以下情形仍会提示或拦截:always_deny规则、always_confirm规则、内置安全规则,以及位于 Zed 设置目录内的路径。
源码侧对应一个“无条件全放行”判断 is_unconditional_allow_all:仅当无 deny/confirm 规则且有效 default 为 allow 时,terminal 的“含 shell 替换即拒绝”检查也会被跳过(对应测试)——即你显式声明“全放行”后,Zed 不再对echo $HOME这类命令额外设防,但仍拦不住内置的rm -rf /类规则(测试)。
路径工具的防穿越:双重判定
对基于路径的工具(edit_file、delete_path、copy_path、move_path 等),Zed 使用 decide_permission_for_paths 而非简单的单次匹配:
- 先对原始路径做一次权限判定;
- 再用 normalize_path 折叠
./..段得到归一化路径,做第二次判定(不触碰文件系统); - 用 most_restrictive(Deny > Confirm > Allow)取两者中最严格的结果。
这意味着src/../.env这类写法无法绕过针对^\.env的 deny 规则——测试族 覆盖了穿越到/etc/passwd、.zed/、.env等多种场景。
权限请求 UI 中的操作
当 Agent 请求权限时,线程视图中的工具卡片会带一个菜单:
- Allow once / Deny once:一次性决定;
- Always for <tool>:把
tools.<tool>.default设为 allow 或 deny; - Always for <pattern>:在能安全抽取模式时,为该输入追加一条
always_allow或always_deny规则; - MCP 工具只支持工具级选项(无 pattern 抽取)。
“Always for <pattern>”抽取出的模式是子命令级精确的:测试 always_allow_button_works_end_to_end 验证了从cargo build --release抽取的模式会放行cargo build --features foo,但不会放行cargo test、cargo build-foo(插件前缀)或npm install。
实战示例集
以下示例完整继承自官方文档,可直接复制到设置文件。
Terminal:自动批准构建命令
{ "agent": { "tool_permissions": { "tools": { "terminal": { "default": "confirm", "always_allow": [ { "pattern": "^cargo\\s+(build|test|check|clippy|fmt)" }, { "pattern": "^npm\\s+(install|test|run|build)" }, { "pattern": "^git\\s+(status|log|diff|branch)" }, { "pattern": "^ls\\b" }, { "pattern": "^cat\\s" } ], "always_deny": [ { "pattern": "rm\\s+-rf\\s+(/|~)" }, { "pattern": "sudo\\s+rm" } ], "always_confirm": [ { "pattern": "sudo\\s" }, { "pattern": "git\\s+push" } ] } } } } }文件编辑:保护敏感文件
{ "agent": { "tool_permissions": { "tools": { "edit_file": { "default": "confirm", "always_allow": [ { "pattern": "\\.(md|txt|json)$" }, { "pattern": "^src/" } ], "always_deny": [ { "pattern": "\\.env" }, { "pattern": "secrets?/" }, { "pattern": "\\.(pem|key)$" } ] } } } } }路径删除:阻断关键目录
{ "agent": { "tool_permissions": { "tools": { "delete_path": { "default": "confirm", "always_deny": [ { "pattern": "^/etc" }, { "pattern": "^/usr" }, { "pattern": "\\.git/?$" }, { "pattern": "node_modules/?$" } ] } } } } }URL 抓取:控制外网访问
{ "agent": { "tool_permissions": { "tools": { "fetch": { "default": "confirm", "always_allow": [ { "pattern": "docs\\.rs" }, { "pattern": "github\\.com" } ], "always_deny": [{ "pattern": "internal\\.company\\.com" }] } } } } }MCP 工具
{ "agent": { "tool_permissions": { "tools": { "mcp:github:create_issue": { "default": "confirm" }, "mcp:github:create_pull_request": { "default": "confirm" } } } } }Skills
skill工具的模式匹配的是该 skillSKILL.md的绝对路径,而不是 skill 名称:
{ "agent": { "tool_permissions": { "tools": { "skill": { "default": "confirm", "always_allow": [{ "pattern": "/code-review/SKILL\\.md$" }] } } } } }要彻底禁止模型调用某个 skill,请在该 skill 的SKILL.md中设置disable-model-invocation: true(见 Skills 文档)。
编写模式的技巧与测试方法
- 用
\b做单词边界:\brm\b匹配 "rm" 但不匹配 "storm"; - 用
^与$将模式锚定到输入首/尾; - 转义特殊字符:字面点号写
\.,反斜杠写\\; - 链式命令中 allow 需要每个子命令都命中——白名单要按“最细粒度命令”来写,而不是按整条流水线。
注意:deny 模式里一个笔误就会误伤合法操作。每个工具的设置页提供 “Test Your Rules” 检查器(实现见 tool_permissions_setup.rs),可以先验证某条命令会落入哪个判定分支再落盘规则。
小结与源码索引
agent.tool_permissions用“六层优先级 + 正则规则 + 不可覆盖的内置底线”三层结构,在 Agent 自动化与操作安全之间给出可精细调节的旋钮。阅读源码建议按以下路径:
- crates/agent/src/tool_permissions.rs:决策引擎
ToolPermissionDecision::from_input、内置安全规则、shell 子命令解析、路径归一化,以及覆盖注入防护/穿越绕过/多路径展开的大规模测试; - crates/agent_settings/src/agent_settings.rs:
ToolPermissions、ToolRules、CompiledRegex(regex 编译与大小写语义)、无效正则的InvalidRegexPattern结构; - crates/settings_content/src/agent.rs:设置文件层面的
ToolPermissionsContent/ToolRulesContent/ToolRegexRule与ToolPermissionMode枚举(allow/deny/confirm,默认 confirm),以及跨设置层累积的注释说明; - assets/settings/default.json:
tool_permissions默认值与注释示例。
【免费下载链接】zedCode at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.项目地址: https://gitcode.com/GitHub_Trending/ze/zed
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考