Distinguish Two Types of Outcomes
Identify explicit Deny versus Hook execution failure — they produce opposite results for the tool call.
Think of Hooks as programmable checkpoints on events. PreToolUse can return an explicit deny; process crashes, timeouts, and unparseable output all go fail-open, letting the tool call proceed.
Identify explicit Deny versus Hook execution failure — they produce opposite results for the tool call.
Master the matcher's exact name, regex pattern, and Bash compatibility alias.
Configure a command Hook following the user guide's JSON structure and design four fault-path tests.
SessionStart, SessionEnd, Stop, StopFailure, PreToolUse, PostToolUse, PostToolUseFailure, PermissionDenied. Only PreToolUse has is_blocking() returning true.
UserPromptSubmit, Notification, SubagentStart, SubagentStop, compatibility alias SubagentEnd, PreCompact, PostCompact.
The event enum defines trigger points; is_blocking() separately declares blocking capability. When reading the event list, also trace how results are returned to the caller.
deny2allow, exit code 2Security Implication: Hooks are appropriate for policy advisories, auditing, and recoverable pre-checks. When enforcement guarantees are required, the permission layer and sandbox should also be used. Source code comments explicitly require that Hook failures must not break tool availability.
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
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)
}
The compatibility mapping lets Bash in configuration hit the internal tool name run_terminal_command. Matchers are compiled from regex; the user guide examples use tool names.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bin/safe-shell-guard.sh",
"timeout": 5
}
]
}
]
}
}
The configuration hierarchy is "event → matcher group → handler list". Commands receive the event envelope via stdin; a valid JSON decision takes priority — if no valid JSON is present, the exit code is interpreted, with exit code 2 expressing denial. Global Hooks live in ~/.grok/hooks/; project Hooks live in .grok/hooks/ and are controlled by folder trust. Reserved environment variables are filtered out, and unresolved variables cause an error before startup.
Deliverable
Config, script, test log
PreToolUse command Hook matching Bash.deny for rm -rf and record the blocked tool result.To judge whether a Hook is safe, ask two questions: can it express an explicit deny, and what does the main flow do when the Hook itself fails? Grok Build's answer is clear — explicit deny blocks, Hook failure goes fail-open.
Source Snapshot Note: This page is based on the hooks crate, user guide, and example configurations from the local grok-build-main snapshot. Code excerpts are for teaching purposes, with log fields and error wrappers omitted; event names, JSON hierarchy, exit codes, and decision semantics match the source.