Grok Build Source Course · 12 / 19

Hooks:明确 deny 才阻断

把 Hook 看成事件上的可编程检查点。PreToolUse 可以返回明确拒绝,进程崩溃、超时和不可解析输出则走 fail-open,让工具调用继续。

15 个事件名PreToolUse 可阻断JSON 配置进程 stdin / stdout
01 / OBJECTIVES

课程目标

分清两类结果

识别显式 Deny 与 Hook 自身执行失败,它们对工具调用产生相反结果。

读懂事件匹配

掌握 matcher 的精确名、正则模式与 Bash 兼容别名。

写出可测试配置

按用户指南的 JSON 结构配置命令 Hook,并设计四条故障测试。

02 / CORE VISUAL

一次 PreToolUse 的决策路径

03 / EVENTS

源码中的事件面

会话与工具

八个主流程检查点

SessionStartSessionEndStopStopFailurePreToolUsePostToolUsePostToolUseFailurePermissionDenied。其中只有 PreToolUseis_blocking() 为真。

用户、代理与压缩

七个扩展检查点

UserPromptSubmitNotificationSubagentStartSubagentStop、兼容别名 SubagentEndPreCompactPostCompact

关键边界

「事件被触发」不等于「能控制主流程」

事件枚举负责定义触发点,is_blocking() 单独声明阻断能力。读取事件列表时,要同时追踪结果如何回到调用方。

crates/codegen/xai-grok-hooks/src/event.rs
04 / SEMANTICS

阻断与 fail-open 矩阵

Hook 结果
dispatcher 解释
工具调用
JSON decision = deny
显式拒绝
阻断
无有效 JSON,退出码 2
fallback 拒绝
阻断
有效 JSON allow,退出码 2
JSON 优先
放行并记录冲突警告
退出码非 0 且非 2
HookRunResult::Failed
放行并记录警告
超时或进程崩溃
HookRunResult::Failed
放行并记录警告
stdout 无效或 decision 未知
回退退出码或 Failed
输出本身不阻断;fallback 退出码 2 仍拒绝

安全含义:Hook 适合策略提醒、审计和可恢复的前置检查。需要强制保证时,还应使用权限层与沙箱。源码注释明确要求 Hook 故障不能破坏工具可用性。

05 / SOURCE

真实源码证据

dispatcher.rs

失败默认放行

match result {
    HookRunnerResult::Decision(
        HookDecision::Deny { reason, .. }
    ) => {
        return PreToolUseResult {
            decision: HookDecision::Deny { ... },
            results: run_results,
        };
    }
    HookRunnerResult::Failed(err) => {
        tracing::warn!(
            error = %err,
            "hook failed; ignoring (fail-open)"
        );
    }
    _ => {}
}
crates/codegen/xai-grok-hooks/src/dispatcher.rs
matcher.rs + command.rs

匹配与退出码

pub const DENY_EXIT_CODE: i32 = 2;

pub fn matches(&self, tool_name: &str) -> bool {
    self.regex.is_match(tool_name)
        || self.matches_compat_alias(tool_name)
}

兼容映射让配置里的 Bash 可命中内部工具名 run_terminal_command。匹配器由正则编译,用户指南示例使用工具名。

crates/codegen/xai-grok-hooks/src/matcher.rs · runner/command.rs
06 / CONFIG

配置按真实 JSON 结构书写

~/.grok/hooks/*.json · project/.grok/hooks/*.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bin/safe-shell-guard.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

配置层级是「事件 → matcher 组 → 处理器列表」。命令从 stdin 接收事件信封;有效 JSON 决策优先,无有效 JSON 时再按退出码解释,退出码 2 表达拒绝。全局 Hook 位于 ~/.grok/hooks/,项目 Hook 位于 .grok/hooks/ 且受 folder trust 控制。保留环境变量会被过滤,未解析变量会在启动前报错。

crates/codegen/xai-grok-hooks/examples/hooks/safe-shell.json · xai-grok-pager/docs/user-guide/10-hooks.md
07 / LAB

课堂练习:验证四条路径

25 MIN

提交物
配置、脚本、测试记录

  1. 配置一个匹配 BashPreToolUse 命令 Hook。
  2. 让脚本对 rm -rf 返回 JSON deny,记录工具被阻断的结果。
  3. 依次制造退出码 1、超时、无效 stdout,验证三者均放行并产生告警。
  4. 将退出码改为 2,再验证无效 stdout 下仍可走明确拒绝路径。
  5. 写一句边界说明:哪条策略必须移到权限层或沙箱。
Takeaway

判断 Hook 是否安全,先问两个问题:它能否表达明确拒绝,以及它自己失效时主流程如何处理。Grok Build 的答案很清楚,显式 deny 阻断,Hook 故障 fail-open。

源码快照说明:本页依据本地 grok-build-main 快照中的 hooks crate、用户指南与示例配置整理。代码片段为教学截取,省略日志字段和错误包装;事件名、JSON 层级、退出码与决策语义保持源码一致。