
第十三篇Permission Model 深度解析 —— Claude Code 如何让 AI 安全执行命令系列文章 第13篇/共100篇· 2026年7月 · ⏱️ 阅读时间约 14 分钟源码位置src/utils/permissions/*、src/types/permissions.ts一、引言为什么需要 Permission ModelClaude Code 不是一个只会聊天的机器人——它会真的在你的机器上跑命令、读写文件、发网络请求。这意味着一个核心问题AI 想做的事哪些可以直接做哪些必须问你哪些永远不允许Permission Model权限模型就是 Claude Code 用来回答这个问题的整套机制。它决定了allow直接放行不打扰你deny直接拒绝AI 碰都碰不到ask弹出权限请求等你点头它是 Claude Code 能在自主能力和安全可控之间取得平衡的关键。没有它AI 要么寸步难行什么都问要么危险至极什么都干。本文从**模式Mode→ 规则Rule→ 判定Check→ 持久化Update→ 企业管控Policy**五个层次拆解这套权限系统。二、整体架构概览┌──────────────────────────────────────────────────────────────┐ │ Permission Model │ ├──────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │ │ │ Permission │ │ Permission │ │ Permission Check │ │ │ │ Mode │ │ Rule │ │ (canUseTool) │ │ │ │ 自主程度 │ │ 规则匹配 │ │ 核心判定引擎 │ │ │ └─────────────┘ └─────────────┘ └──────────────────────┘ │ ├──────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │ │ │ Update │ │ Denial Track│ │ Policy / Managed │ │ │ │ 记住选择 │ │ 拒绝兜底 │ │ 企业管控 │ │ │ └─────────────┘ └─────────────┘ └──────────────────────┘ │ └──────────────────────────────────────────────────────────────┘ │ ▼ Tool 执行 or 权限请求弹窗数据流AI 请求调用 Tool(input) │ ▼ canUseTool(tool, input, ctx) │ ├── Mode 短路bypass / dontAsk / plan ├── allow 规则命中 ──► allow直接执行 ├── deny 规则命中 ──► deny拒绝 ├── hook 前置拦截 ──► deny / ask ├── classifier 判定 ──► allow / deny / ask └── 都不命中 ──► ask弹窗等你确认 │ ▼ 用户选择本次/始终允许/拒绝 │ ▼ applyPermissionUpdate → persist写入 settings三、Permission ModeAI 的自主程度权限模型最外层是模式Mode——它定义了 AI 整体有多自由。源码在src/utils/permissions/PermissionMode.ts和src/types/permissions.ts。3.1 模式定义// 外部可见的模式settings.json / --permission-mode 可用exportconstEXTERNAL_PERMISSION_MODES[acceptEdits,// 自动接受文件编辑bypassPermissions,// 跳过所有权限检查default,// 默认按规则 询问dontAsk,// 不问危险直接执行无确认plan,// 计划模式只思考不执行]asconst// 内部模式额外包含 ant 专属的 auto / bubbleexporttypeInternalPermissionMode|ExternalPermissionMode|auto|bubble每个模式有展示配置constPERMISSION_MODE_CONFIG{default:{title:Default,external:default},plan:{title:Plan Mode,external:plan,symbol:PAUSE_ICON},acceptEdits:{title:Accept edits,external:acceptEdits},bypassPermissions:{title:Bypass Permissions,external:bypassPermissions},dontAsk:{title:Dont Ask,external:dontAsk},// auto: ant-only 的自动模式通过 TRANSCRIPT_CLASSIFIER feature gate 开启}3.2 模式怎么切换终端里按ShiftTab循环切换模式逻辑在getNextPermissionMode()// 默认循环default → acceptEdits → plan → bypassPermissions → (auto?) → defaultexportfunctiongetNextPermissionMode(ctx,_team?):PermissionMode{switch(ctx.mode){casedefault:returnacceptEdits// 先开放编辑caseacceptEdits:returnplan// 再收紧到只读计划caseplan:returnctx.isBypassPermissionsModeAvailable?bypassPermissions// 再放开到全放行:(canCycleToAuto(ctx)?auto:default)// ...}} 实战价值bypassPermissions和dontAsk是双刃剑——CI / 脚本场景极方便但本地日常使用务必谨慎因为它们会绕过所有确认。四、Permission Rule规则长什么样模式是全局开关规则才是精细控制。规则的核心类型在src/types/permissions.tsexporttypePermissionBehaviorallow|deny|ask// 一条规则 来源 行为 内容exporttypePermissionRule{source:PermissionRuleSource// 这条规则从哪来ruleBehavior:PermissionBehavior ruleValue:PermissionRuleValue}exporttypePermissionRuleValue{toolName:string// 作用于哪个工具如 Bash / Edit / WebFetchruleContent?:string// 可选的内容匹配如 npm install / src/**}4.1 规则来源与优先级规则可以从多个地方来源码用PERMISSION_RULE_SOURCES定义了叠加顺序constPERMISSION_RULE_SOURCES[...SETTING_SOURCES,// userSettings → projectSettings → localSettings → policySettingscliArg,// 命令行 --allowedToolscommand,// 本次会话命令注入session,// 会话内临时规则]asconstexporttypePermissionRuleSource|userSettings|projectSettings|localSettings|flagSettings|policySettings|cliArg|command|session配置文件里这样写settings.json{permissions:{allow:[Read,Edit,Bash(git status),Bash(npm test)],deny:[Bash(rm -rf *),WebFetch],ask:[Bash(curl *)]}}4.2 规则字符串解析ToolName(content)规则在磁盘上存成字符串格式ToolName或ToolName(content)解析在permissionRuleParser.tspermissionRuleValueFromString(Bash)// { toolName: Bash }permissionRuleValueFromString(Bash(npm install))// { toolName: Bash, ruleContent: npm install }内容里可能有括号如命令需要转义// 转义顺序很重要先转义反斜杠再转义括号escapeRuleContent(psycopg2.connect())// psycopg2.connect\\(\\)unescapeRuleContent(psycopg2.connect\\(\\))// psycopg2.connect()工具改名也能兼容旧名自动映射到新名constLEGACY_TOOL_NAME_ALIASES{Task:AGENT_TOOL_NAME,// Task → AgentKillShell:TASK_STOP_TOOL_NAME,AgentOutputTool:TASK_OUTPUT_TOOL_NAME,BashOutputTool:TASK_OUTPUT_TOOL_NAME,}normalizeLegacyToolName(Task)// Agent五、核心判定canUseTool 如何决策真正拍板的地方是src/utils/permissions/permissions.ts里的判定逻辑概念模型functioncanUseTool(tool,input,ctx):PermissionDecision{// 1) 模式短路if(ctx.modebypassPermissions)returnallow(input)// 全放行if(ctx.modeplantool.mutates)returndeny(计划模式禁止修改)// 2) 收集并匹配 allow 规则constallowRulesgetAllowRules(ctx)// 跨所有 source 摊平for(construleofmatchRules(tool,input,allowRules)){returnallow(input)}// 3) deny 规则命中 → 直接拒绝if(matchDenyRules(tool,input,ctx))returndeny(规则拒绝)// 4) hook 前置拦截PreToolUse hook 可 deny / ask// 5) classifier 判定BashClassifier / YOLO 分类器// 6) 以上都不命中 → 询问用户returnask(buildReason(tool,ctx))}5.1 决策来源decisionReason当弹出权限请求时Claude Code 会告诉用户为什么需要确认来源有三类typePermissionDecisionReason|{type:rule;rule:PermissionRule}// 被某条规则命中|{type:hook;hookName:string;reason?:string}// 被 hook 拦截|{type:classifier;classifier:string;reason:string}// 被分类器判定拼成用户能看懂的提示语exportfunctioncreatePermissionRequestMessage(toolName,reason?){if(reason?.typehook){returnHook ${reason.hookName} 要求确认此${toolName}操作}if(reason?.typerule){return根据规则${permissionRuleValueToString(reason.rule.ruleValue)}需要确认}// classifier ...}六、决策结果allow / deny / ask判定引擎返回统一的PermissionDecisionexporttypePermissionDecision|PermissionAllowDecision|PermissionDenyDecision|PermissionAskDecisionexporttypePermissionAllowDecision{behavior:allowupdatedInput:Recordstring,unknown// 可能改写入参updatedPermissions?:PermissionUpdate[]// 附带要记录的权限变更}exporttypePermissionDenyDecision{behavior:denymessage:stringinterrupt?:boolean}七、记住这次选择权限更新与持久化用户点始终允许时Claude Code 不是只管这一次而是把规则写回 settings。这是PermissionUpdate机制exporttypePermissionUpdate|{type:addRules;destination:PermissionUpdateDestination;rules:PermissionRuleValue[];behavior:PermissionBehavior}|{type:replaceRules;destination:PermissionUpdateDestination;rules:PermissionRuleValue[];behavior:PermissionBehavior}|{type:removeRules;destination:PermissionUpdateDestination;rules:PermissionRuleValue[];behavior:PermissionBehavior}|{type:setMode;destination:PermissionUpdateDestination;mode:ExternalPermissionMode}exporttypePermissionUpdateDestination|userSettings|projectSettings|localSettings|session|cliArg执行时applyPermissionUpdate(update,ctx)// 内存生效本次会话persistPermissionUpdates([update],ctx)// 落盘下次启动也有效持久化读取用了宽松解析避免因无关字段校验失败而丢失已有规则// permissionsLoader.ts只解析 JSON、不强制校验仅用于追加规则functiongetSettingsForSourceLenient_FOR_EDITING_ONLY_NOT_FOR_READING(source){constcontentreadFileSync(getSettingsFilePathForSource(source))returnsafeParseJSON(content,false)// 保留所有现有配置}7.1 用户分类临时 vs 永久在 SDK / MCP 权限提示里用户选择会带上分类decisionClassification:user_temporary// 仅本次允许|user_permanent// 永久记住写入 settings|user_reject// 永久拒绝八、拒绝追踪与兜底连续拒绝可能意味着规则配置有问题。Claude Code 用denialTracking.ts做拒绝计数超过阈值就自动降级为询问而非硬拒避免把 AI 彻底卡死constDENIAL_LIMITS{/* 按工具/会话维度的拒绝上限 */}recordDenial(tool)// 记一次拒绝if(shouldFallbackToPrompting(state)){// 超阈值// 不再硬拒改为继续询问用户}九、SDK / MCP 权限提示协议当 Claude Code 作为库SDK或接入外部 MCP 服务器时权限请求通过工具协议传递定义在PermissionPromptToolResultSchema.ts// 宿主收到的请求inputSchemaz.object({tool_name:z.string(),// 请求权限的工具名input:z.record(z.string(),z.unknown()),// 工具入参tool_use_id:z.string().optional(),// 请求 ID})// 宿主返回的决策outputSchemaz.union([z.object({behavior:z.literal(allow),updatedInput:z.record(z.string(),z.unknown()),updatedPermissions:z.array(permissionUpdateSchema()).optional(),decisionClassification:z.enum([user_temporary,user_permanent,user_reject]).optional(),}),z.object({behavior:z.literal(deny),message:z.string(),interrupt:z.boolean().optional(),}),]) 设计哲学把权限决策抽象成工具调用使 SDK 宿主、MCP 服务器都能插入自己的审批逻辑——这是 Claude Code 能嵌入 VS Code、CI、Agent 平台的基础。十、企业管控policySettings 与托管规则企业环境需要用户不能绕过的安全基线。permissionsLoader.ts提供// 仅允许托管policy规则生效忽略用户本地 allow 规则exportfunctionshouldAllowManagedPermissionRulesOnly():boolean{returngetSettingsForSource(policySettings)?.allowManagedPermissionRulesOnlytrue}// 开启后权限弹窗里隐藏始终允许选项exportfunctionshouldShowAlwaysAllowOptions():boolean{return!shouldAllowManagedPermissionRulesOnly()}这让管理员可以强制deny某些危险命令且用户无法用始终允许绕过——安全策略真正兜底。十一、源码亮点总结特性实现位置价值模式分级PermissionMode.tsdefault/plan/acceptEdits/bypass/dontAsk 灵活切换规则三态types/permissions.tsallow/deny/ask 覆盖全部场景多源叠加PERMISSION_RULE_SOURCESuser/project/local/policy/cli/session 优先级清晰字符串解析permissionRuleParser.tsTool(content) 括号转义 旧名兼容判定引擎permissions.ts模式短路 → 规则 → hook → classifier → ask持久化PermissionUpdate.ts用户选择写回 settings跨会话生效拒绝兜底denialTracking.ts超阈值自动降级为询问协议化PermissionPromptToolResultSchema.tsSDK/MCP 可插入自定义审批企业管控permissionsLoader.tsallowManagedPermissionRulesOnly锁定基线十二、下一篇预告第14篇我们将深入上下文管理Context Management——200K token 窗口怎么被高效利用Claude Code 如何在长对话里保留关键信息、丢弃噪声。敬请期待Claude Code 源码解析系列源码泄露事件深度解析开发环境搭建指南CLI入口与命令系统AI对话引擎全解析工具系统深度解析CLI命令行解析核心Handler处理器链QueryEngine查询引擎query.ts API对话层callModel.ts API调用层AnthropicClient API客户端MessageBuilder 消息构建器Permission Model 权限模型← 本文Context Management 上下文管理待续如果这篇文章对你有帮助欢迎点赞、收藏、关注❓有问题欢迎在评论区讨论#ClaudeCode #源码分析 #PermissionModel #权限模型 #安全 #TypeScript #AI编程