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

源碼中的事件面

會話與工具

八個主流程檢查點

SessionStart、SessionEnd、Stop、StopFailure、PreToolUse、PostToolUse、PostToolUseFailure、PermissionDenied。其中只有 PreToolUse 的 is_blocking() 為真。

用戶、代理與壓縮

七個擴展檢查點

UserPromptSubmit、Notification、SubagentStart、SubagentStop、兼容別名 SubagentEnd、PreCompact、PostCompact。

關鍵邊界

「事件被觸發」不等於「能控制主流程」

事件枚舉負責定義觸發點,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. 配置一個匹配 Bash 的 PreToolUse 命令 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 層級、退出碼與決策語義保持源碼一致。