第四节:Hooks 与权限系统
安全与权限控制

作者:小学子 📚 | 日期:2026年4月2日 | 第六阶段 · 模块六


# 第六阶段 · 模块六 · 第四节:Hooks 与权限系统

核心问题

Hook 如何影响权限决策?PermissionRequest 的工作流程是什么?如何通过 Hook 实现细粒度权限控制?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  Hooks 系统                                                          │
        │                                                                      │
        │  Hooks ↔ 权限系统 ← 本节                                            │
        │  ├── Hook 拦截权限请求                                              │
        │  ├── 权限规则影响 Hook                                             │
        │  └── 协作流程                                                        │
        └─────────────────────────────────────────────────────────────────────┘
        


一、权限系统概述

1.1 权限类型


        Claude Code 权限模型:
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  权限类型                                                            │
        │                                                                      │
        │  Read ──> 读取文件/目录                                             │
        │  Write ──> 写入文件                                                 │
        │  Bash ──> 执行命令                                                  │
        │  Browser ──> 浏览器操作                                             │
        │  MCP ──> MCP 服务器访问                                             │
        └─────────────────────────────────────────────────────────────────────┘
        

1.2 权限决策点


        工具调用流程:
        
        Tool Call → Permission Check → [Hook] → Allow/Deny/Prompt
        
                            ↑
                  Hook 可以在此处拦截
        

1.3 五问分析

问 1:Hook 和权限系统谁先执行?


        // Hook 先于权限系统执行
        // 顺序:PreToolUse Hook → Permission Check → PostToolUse Hook
        
        // 如果 PreToolUse 返回 continue: false
        //   → 不进入权限系统(直接拒绝)
        
        // 如果 PreToolUse 返回 permissionDecision
        //   → 影响权限决策
        


二、PermissionRequest Hook

2.1 触发时机

源码位置:`src/types/hooks.ts` 第 121 行


        权限检查 → 需要用户授权 → PermissionRequest Hook
        

2.2 输入结构


        type PermissionRequestInput = {
          hookEventName: 'PermissionRequest';
          toolName: string;
          toolInput: Record<string, unknown>;
          permission: {
            type: string;      // 'Read' | 'Write' | 'Bash'...
            reason: string;    // 为什么需要这个权限
          };
          prompt?: string;     // 给用户看的提示
          invocationId: string;
        };
        

2.3 返回结构

源码位置:`src/types/hooks.ts` 第 248 行


        type PermissionRequestResult =
          | {
              behavior: 'allow';
              updatedInput?: Record<string, unknown>;
              updatedPermissions?: PermissionUpdate[];
            }
          | {
              behavior: 'deny';
              message?: string;
              interrupt?: boolean;
            };
        

2.4 使用示例


        // hooks/permission-controller.ts
        export const permissionRequest: HookHandler = async (input, context) => {
          const { toolName, toolInput, permission } = input;
        
          // 自动批准安全操作
          if (toolName === 'Read') {
            const path = toolInput.file_path as string;
        
            // 只允许读取 src 和 docs
            if (path.startsWith('/project/src') || path.startsWith('/project/docs')) {
              return {
                permissionRequestResult: {
                  behavior: 'allow',
                },
              };
            }
        
            // 阻止读取 .env
            if (path.includes('.env')) {
              return {
                permissionRequestResult: {
                  behavior: 'deny',
                  message: 'Cannot read .env files for security reasons',
                  interrupt: true,
                },
              };
            }
          }
        
          // 阻止危险命令
          if (toolName === 'Bash') {
            const cmd = toolInput.command as string;
        
            if (cmd.includes('sudo') || cmd.includes('chmod 777')) {
              return {
                permissionRequestResult: {
                  behavior: 'deny',
                  message: 'This command is not allowed',
                  interrupt: true,
                },
              };
            }
          }
        
          // 其他情况让权限系统决定
          return { continue: true };
        };
        


三、权限与 Hook 的协作

3.1 协作流程


        ┌─────────────────────────────────────────────────────────────────────┐
        │  完整权限流程                                                        │
        │                                                                      │
        │  1. PreToolUse Hook                                                │
        │     ├─ 可修改输入                                                    │
        │     └─ 可返回 continue: false(阻止)                                │
        │                                                                      │
        │  2. 权限检查                                                        │
        │     ├─ 检查权限规则                                                  │
        │     └─ 决定是否需要用户授权                                          │
        │                                                                      │
        │  3. PermissionRequest Hook(如果需要授权)                           │
        │     ├─ 可自动批准                                                    │
        │     ├─ 可自动拒绝                                                    │
        │     └─ 可修改输入                                                    │
        │                                                                      │
        │  4. 工具执行                                                        │
        │     └─ 用户批准后执行                                               │
        │                                                                      │
        │  5. PostToolUse Hook                                               │
        │     └─ 处理结果                                                      │
        └─────────────────────────────────────────────────────────────────────┘
        

3.2 五问分析

问 1:Hook 如何影响权限决策?


        // 通过 permissionDecision
        return {
          continue: true,
          permissionDecision: {
            behavior: 'allow',  // 自动批准
            // 或
            behavior: 'deny',   // 自动拒绝
          },
        };
        

问 2:Hook 修改的输入会影响权限吗?


        // 是的
        // 如果 Hook 修改了 toolInput,权限检查会使用修改后的值
        
        // 示例:限制文件路径
        {
          updatedInput: { file_path: '/safe/path.txt' }
        // → 权限检查通过,因为路径已修改为安全的
        }
        


四、细粒度权限控制

4.1 基于路径的权限


        // hooks/path-permissions.ts
        const ALLOWED_PATHS = {
          Read: ['/project/src', '/project/docs', '/tmp'],
          Write: ['/project/src', '/project/build'],
          Bash: ['/project/scripts', '/usr/bin'],
        };
        
        export const permissionRequest: HookHandler = async (input) => {
          const { toolName, toolInput } = input;
          const allowed = ALLOWED_PATHS[toolName] || [];
        
          if (toolName === 'Read' || toolName === 'Write') {
            const path = toolInput.file_path as string;
        
            if (!allowed.some(p => path.startsWith(p))) {
              return {
                permissionRequestResult: {
                  behavior: 'deny',
                  message: `Path not allowed: ${path}`,
                  interrupt: true,
                },
              };
            }
          }
        
          return { continue: true };
        };
        

4.2 基于内容的权限


        // hooks/content-permissions.ts
        export const permissionRequest: HookHandler = async (input) => {
          const { toolName, toolInput, permission } = input;
        
          // 检查 Write 操作的内容
          if (toolName === 'Write') {
            const content = toolInput.content as string;
        
            // 阻止写入敏感信息
            if (content.includes('password') || content.includes('api_key')) {
              return {
                permissionRequestResult: {
                  behavior: 'deny',
                  message: 'Cannot write sensitive content',
                  interrupt: true,
                },
              };
            }
          }
        
          return { continue: true };
        };
        

4.3 基于时间的权限


        // hooks/time-permissions.ts
        const WORK_HOURS = { start: 9, end: 18 };  // 9 AM - 6 PM
        
        export const permissionRequest: HookHandler = async (input) => {
          const now = new Date();
          const hour = now.getHours();
        
          // 非工作时间需要额外确认
          if (hour < WORK_HOURS.start || hour > WORK_HOURS.end) {
            if (input.toolName === 'Bash' || input.toolName === 'Write') {
              return {
                permissionRequestResult: {
                  behavior: 'deny',
                  message: 'This operation requires approval outside work hours',
                  interrupt: true,
                },
              };
            }
          }
        
          return { continue: true };
        };
        


五、安全 Hook 实践

5.1 最小权限原则


        // 只授权必要的权限
        export const permissionRequest: HookHandler = async (input) => {
          const { toolName, toolInput } = input;
        
          // Read 默认允许(但限制路径)
          if (toolName === 'Read') {
            return { continue: true };
          }
        
          // Write 需要明确检查
          if (toolName === 'Write') {
            // 检查是否写入临时目录
            const path = toolInput.file_path as string;
            if (path.startsWith('/tmp')) {
              return { continue: true };
            }
            // 其他 Write 操作需要授权
            return { continue: true };
          }
        
          return { continue: true };
        };
        

5.2 审计日志


        // hooks/audit.ts
        import { appendFile } from 'fs/promises';
        import { join } from 'path';
        
        export const permissionRequest: HookHandler = async (input) => {
          const logEntry = {
            timestamp: new Date().toISOString(),
            event: 'permission_request',
            tool: input.toolName,
            permission: input.permission,
            user: input.userIdentity,
          };
        
          await appendFile(
            '/var/log/claude-audit.json',
            JSON.stringify(logEntry) + '\n'
          );
        
          return { continue: true };
        };
        

5.3 防止 Hook 绕过


        // 错误示例:Hook 不能完全阻止权限系统
        export const permissionRequest: HookHandler = async (input) => {
          // ❌ 错误:直接返回 allow 可能绕过安全检查
          return { permissionRequestResult: { behavior: 'allow' } };
        
          // ✓ 正确:让权限系统做最终决定
          return { continue: true };
        
          // ✓ 正确:只在明确危险时拒绝
          if (isDangerous(input)) {
            return { permissionRequestResult: { behavior: 'deny' } };
          }
          return { continue: true };
        };
        


六、思考题

思考题 1:Hook 失败会导致权限拒绝吗?

答案


        // 如果 Hook 执行出错
        // 默认行为取决于配置
        // 通常:跳过该 Hook,继续执行
        
        // 但如果在 PreToolUse 中抛出异常
        // 可能导致整个操作失败
        
        // 最佳实践:捕获所有可能的错误
        export const permissionRequest: HookHandler = async (input) => {
          try {
            return await processPermission(input);
          } catch (error) {
            console.error('Hook error:', error);
            // 失败时拒绝(保守策略)
            return {
              permissionRequestResult: {
                behavior: 'deny',
                message: 'Internal error',
              },
            };
          }
        };
        


思考题 2:如何实现临时权限?

答案


        // 使用内存存储临时权限
        const tempPermissions = new Map<string, { expires: number }>();
        
        export const permissionRequest: HookHandler = async (input) => {
          const sessionId = input.sessionId;
          const temp = tempPermissions.get(sessionId);
        
          // 检查临时权限
          if (temp && Date.now() < temp.expires) {
            return { continue: true };
          }
        
          // 检查是否需要授予临时权限
          if (input.toolName === 'Bash' && isSafeCommand(input.toolInput)) {
            // 授予 5 分钟临时权限
            tempPermissions.set(sessionId, {
              expires: Date.now() + 5 * 60 * 1000,
            });
            return { continue: true };
          }
        
          return { continue: true };
        };
        


思考题 3:Hook 和权限规则冲突怎么办?

答案


        // 权限规则 > Hook
        // 如果权限规则明确拒绝,Hook 无法覆盖
        
        // Hook 的行为:
        // 1. Hook 可以拒绝(deny)
        // 2. Hook 可以批准(allow)
        // 3. Hook 不能覆盖明确的权限规则拒绝
        
        // 优先级(高 → 低):
        // 1. 权限规则(deny)
        // 2. Hook(deny)
        // 3. Hook(allow)
        // 4. 权限规则(allow)
        


七、延伸阅读

文件核心内容
`src/types/hooks.ts`Hook 类型定义
`src/utils/permissions/`权限系统实现


八、下节预告

下一节我们将进入 第七章:MCP 系统

- MCP 协议概述

- MCP 客户端实现

- MCP 工具集成


*- 第一轮:□ 事实准确性*

*- 第二轮:□ 深度与洞见*

*- 第三轮:□ 可读性与价值*