Hooks:明确 deny 才阻断
核对生命周期事件、matcher、PreToolUse 阻断和故障 fail-open 语义
本页解决的问题
先给结论「Hooks:明确 deny 才阻断」要解决的关键问题是什么?
核对生命周期事件、matcher、PreToolUse 阻断和故障 fail-open 语义
让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。
写下一个问题:试完这个方法后,你能用什么证据回答它?
结论听起来很完整,却没有检查最关键的假设。
Hooks:明确 deny 才阻断
把 Hook 看成事件上的可编程检查点。PreToolUse 可以返回明确拒绝,进程崩溃、超时和不可解析输出则走 fail-open,让工具调用继续。
课程目标
分清两类结果
识别显式 Deny 与 Hook 自身执行失败,它们对工具调用产生相反结果。
读懂事件匹配
掌握 matcher 的精确名、正则模式与 Bash 兼容别名。
写出可测试配置
按用户指南的 JSON 结构配置命令 Hook,并设计四条故障测试。
一次 PreToolUse 的决策路径
源码中的事件面
八个主流程检查点
SessionStart、SessionEnd、Stop、StopFailure、PreToolUse、PostToolUse、PostToolUseFailure、PermissionDenied。其中只有 PreToolUse 的 is_blocking() 为真。
七个扩展检查点
UserPromptSubmit、Notification、SubagentStart、SubagentStop、兼容别名 SubagentEnd、PreCompact、PostCompact。
「事件被触发」不等于「能控制主流程」
事件枚举负责定义触发点,is_blocking() 单独声明阻断能力。读取事件列表时,要同时追踪结果如何回到调用方。
阻断与 fail-open 矩阵
deny2allow,退出码 2安全含义:Hook 适合策略提醒、审计和可恢复的前置检查。需要强制保证时,还应使用权限层与沙箱。源码注释明确要求 Hook 故障不能破坏工具可用性。
真实源码证据
失败默认放行
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)
}
兼容映射让配置里的 Bash 可命中内部工具名 run_terminal_command。匹配器由正则编译,用户指南示例使用工具名。
配置按真实 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 控制。保留环境变量会被过滤,未解析变量会在启动前报错。
课堂练习:验证四条路径
提交物
配置、脚本、测试记录
- 配置一个匹配
Bash的PreToolUse命令 Hook。 - 让脚本对
rm -rf返回 JSONdeny,记录工具被阻断的结果。 - 依次制造退出码 1、超时、无效 stdout,验证三者均放行并产生告警。
- 将退出码改为 2,再验证无效 stdout 下仍可走明确拒绝路径。
- 写一句边界说明:哪条策略必须移到权限层或沙箱。
判断 Hook 是否安全,先问两个问题:它能否表达明确拒绝,以及它自己失效时主流程如何处理。Grok Build 的答案很清楚,显式 deny 阻断,Hook 故障 fail-open。
源码快照说明:本页依据本地 grok-build-main 快照中的 hooks crate、用户指南与示例配置整理。代码片段为教学截取,省略日志字段和错误包装;事件名、JSON 层级、退出码与决策语义保持源码一致。
「Hooks: 明确 deny 才阻断」里的风险边界在哪里
「把 Hook 看成事件上的可编程检查点。」把安全问题从一句“请模型不要犯错”拉回到权限、数据和环境。真正需要保护的是:模型即使判断失误,系统也不能让错误变成不可逆的结果。
把模型建议和实际权限分开
在「识别显式 Deny 与 Hook 自身执行失败,它们对工具调用产生相反结果」涉及的流程中,要分别检查用户能要求什么、模型能建议什么、工具实际允许什么,以及谁有权批准写入或发送。网页、文档和工具返回值都可能携带不可信指令,不能因为它们看起来像说明就自动提升权限。
- 配置一个匹配 Bash 的 PreToolUse 命令 Hook
- 让脚本对 rm -rf 返回 JSON deny ,记录工具被阻断的结果
- 依次制造退出码 1、超时、无效 stdout,验证三者均放行并产生告警
安全设计必须包含失败和恢复
结合「源码快照说明: 本页依据本地 grok-build-main 快照中的 hooks crate、用户指南与示例配置整理。代码片段为教学截取,省略日志字段和错误包装;事件名、JSON 层级、退出码与决策语义保持源码一致」做一次反向演练:加入错误输入、缺失凭证或迟迟不到的审批,确认系统会拒绝、暂停并留下可追踪信息,而不是继续执行到底。
从「Hooks: 明确 deny 才阻断」走到「分清两类结果」
「Hooks: 明确 deny 才阻断」先把问题落在「把 Hook 看成事件上的可编程检查点。 PreToolUse 可以返回明确拒绝,进程崩溃、超时和不可解析输出则走 fail-open,让工具调用继续」上;到了「分清两类结果」,讨论继续推进到「识别显式 Deny 与 Hook 自身执行失败,它们对工具调用产生相反结果」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
做安全判断时,把“模型想做什么”和“系统允许做什么”分开,逐个检查数据边界、工具权限、人工确认和失败后的恢复路径。
- 「Hooks: 明确 deny 才阻断」:把 Hook 看成事件上的可编程检查点。 PreToolUse 可以返回明确拒绝,进程崩溃、超时和不可解析输出则走 fail-open,让工具调用继续
- 「分清两类结果」:识别显式 Deny 与 Hook 自身执行失败,它们对工具调用产生相反结果
- 「最后的要点」:写一句边界说明:哪条策略必须移到权限层或沙箱
最后的「最后的要点」把讨论落到「写一句边界说明:哪条策略必须移到权限层或沙箱」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。