Skip to content

19|权限体系:允许做什么

上一篇的工具接口声明了能力边界——工具能做什么。本篇讲"被允许做什么"。

只读工具也可能读到不该读的内容(cat /etc/shadow 也是只读),破坏性工具需要更严格的审批。能力边界不等于权限边界。权限体系用工具的声明,加上当前权限模式,决定每次工具调用要不要放行。

六种权限模式

不同场景需要不同的默认严格度。六种权限模式覆盖从最严到最松:

AIOps 概念图:六种权限模式

模式行为适用场景
plan只能规划,不能执行写操作探索、审查、设计
default工具调用需要用户确认日常开发
acceptEdits工作目录内编辑自动允许,其他仍确认较信任的重构场景
auto分类器自动判断安全性高信任场景
bypassPermissions跳过大部分检查,保留硬编码安全线受控环境、紧急修复
dontAsk需要确认的操作直接拒绝自动化、CI/CD

planbypassPermissions 信任度递增。dontAsk 比较特殊——它不是更松,是"该问的直接拒",用于无人值守的自动化场景,避免卡在等确认上。

权限判断是一条管线

权限判断不是单点 if,是一条有顺序的管线。顺序很重要,搞反了就不安全。

AIOps 概念图:权限判断管线

python
def evaluate_permission(tool_call, context, mode) -> tuple[bool, str]:
    # 1. 用户显式 ask 规则优先
    verdict = check_user_rules(tool_call, context)
    if verdict:
        return verdict

    # 2. 敏感路径额外保护
    if is_sensitive_path(tool_call):
        return False, "敏感路径受保护"

    # 3. 按当前模式判断
    if mode == "plan" and not tool_call.tool.is_read_only:
        return False, "plan 模式禁止写操作"
    if mode == "default":
        return ask_user(tool_call), "等待用户确认"
    if mode == "auto":
        return classifier_judge(tool_call), "分类器判断"
    if mode == "bypassPermissions":
        return check_hardcoded_limits(tool_call), "bypass 但保留硬编码安全线"
    if mode == "dontAsk":
        return False, "dontAsk 模式拒绝需确认操作"

用户显式规则最优先。即使用户设了 bypassPermissions,如果他显式 ask 某个操作要确认,这条 ask 规则优先于 bypass。这保证了用户对特定操作的控制权不被高信任模式覆盖。

敏感路径额外保护:.git 目录、编辑器配置、shell 配置这些路径,无论什么模式都要额外审查。改 .git 可能破坏版本库,改 shell 配置可能被注入恶意命令。

最后才按当前模式判断。模式决定默认严格度,但前两步的规则优先于模式。

bypass 不是万能钥匙

很多开发者误以为 bypassPermissions 可以跳过所有检查,在生产环境随意使用。这是危险的误解。

bypass 跳过的是"弹窗确认",不是"安全检查"。它保留硬编码安全线——禁止删除 .git 目录、禁止修改系统文件、禁止某些破坏性操作。这些安全线不受任何模式影响,bypass 也绕不过。

python
HARDCODED_DENY = [
    r"\.git/.*",
    r"/etc/.*",
    r"rm -rf /",
]

def check_hardcoded_limits(tool_call):
    for pattern in HARDCODED_DENY:
        if matches(tool_call, pattern):
            return False, f"硬编码安全线:禁止 {pattern}"
    return True, "bypass 通过"

bypass 的设计意图是受控环境里的紧急修复——你明确知道在干什么,不想被反复确认打断。它不是"我懒得分权限,全放行"的借口。生产环境用 bypass 要非常谨慎。

auto 模式的分类器

auto 模式靠分类器自动判断操作安全性。它不是"替代人工判断",是"减少低价值的人工确认"。

分类器判断为安全的操作,通常是重复性高、风险低、历史记录良好的操作(读取常见日志文件)。新颖的、少见的、高风险的操作仍然推给人工确认。分类器会错,所以高风险操作不依赖分类器,仍走人工。

连续被拒触发降级

auto 模式下,如果模型连续被拒,说明它在不断换方式尝试危险操作。这时不该继续让它试,要触发降级或终止。

AIOps 概念图:连续拒绝后降级

python
if context.consecutive_rejections >= 3:
    degrade_mode()   # 降级到更严格的模式,或终止会话

这和第 14 篇的停止条件呼应——连续被拒本身就是一种停止信号。不触发降级的话,模型可能反复尝试不同写法绕过权限,消耗 token 又制造风险。

授权不传递

第 4 篇讲过这条约束,这里从权限体系角度再强调:用户对单次操作的批准,不延伸到后续类似操作。

用户这次批准了"重启 nginx",不代表下次遇到 nginx 问题可以不问直接重启。每次写操作都要单独确认。这条规则要写进权限管线——批准状态不缓存、不泛化。

规则遮蔽检测

权限规则有来源(用户配置、项目配置、企业策略)和匹配策略(精确、前缀、通配)。高优先级的 deny 规则可能让后续的 allow 永远不生效:

text
企业策略:deny edit_config *
用户配置:allow edit_config nginx.conf   ← 永远不生效,被企业 deny 遮蔽

系统要检测这种遮蔽,在配置加载时提醒用户"这条 allow 规则被更高优先级的 deny 遮蔽了,不会生效"。否则用户以为某操作被允许了,实际还是被拒,排查时一头雾水。

企业策略必须是不可编辑的。如果企业级 denylist 能被个人用户覆盖,整个权限体系就失去了组织层面的保障。企业策略启动时加载校验,运行时只读。

权限之后

权限体系让 agent 的每个动作都受控。但有一类工具特别难管:shell 命令。shell 表达力太强,一个 ; 就能拼接出任意命令,权限管线很难直接判断它的安全性。

下一篇专门讲 BashTool 安全。它是权限体系里最复杂的工具,需要 AST 解析、白名单、注入检测一套专门的处理。