跳转到正文

钩子与治理

本页介绍 .github/hooks/目录下的治理钩子。这些钩子为 Agent HQ 演示添加会话日志、工具调用前安全门禁、工具调用后审计日志和会话摘要。

retail-governance.json 架构

.github/hooks/retail-governance.json 声明了一个简单的钩子配置:

  • version:数字形式的配置版本,当前为 1
  • hooks:一个映射,其中每个键都是生命周期事件名称。
  • 每个事件的值都是命令条目数组。
  • 每个命令条目包含:
  • type:当前为 command
  • bash:要执行的脚本路径。
  • cwd:命令的工作目录。
  • timeoutSec:最长运行时间,单位为秒。

示例条目:

{
  "type": "command",
  "bash": "./.github/hooks/scripts/security-gate.sh",
  "cwd": ".",
  "timeoutSec": 15
}

生命周期事件

治理文件将四个生命周期事件连接到四个 Shell 脚本:

事件 脚本 用途
sessionStart .github/hooks/scripts/session-init.sh 初始化会话日志
preToolUse .github/hooks/scripts/security-gate.sh 允许或拒绝工具使用
postToolUse .github/hooks/scripts/audit-logger.sh 追加 JSONL 审计事件
sessionEnd .github/hooks/scripts/session-end.sh 写入会话摘要

脚本行为

session-init.sh

session-init.sh 从标准输入读取钩子 JSON。它提取 .source.timestamp.cwd(使用 jq),创建 logs/,并追加带格式的 SESSION START 块至 logs/session.log ,其中包含 UTC 时间、来源、当前工作目录和当前操作系统用户。

security-gate.sh

security-gate.shpreToolUse 门禁。它从标准输入读取 JSON,并提取 .toolName.toolArgs(使用 jq)。

对于 bash,它会拒绝匹配破坏性模式的命令:

  • rm -rf /
  • rm -rf .
  • DROP TABLE
  • DROP DATABASE
  • format
  • mkfs.
  • 包含以下内容的 fork 炸弹语法: :(){

它还会拒绝引用凭据或机密路径及相关术语的 bash 命令:

  • .env
  • credentials
  • secrets
  • .pem
  • .key
  • password

对于 editcreate,它会提取 .toolArgs.path。不匹配钩子允许列表表达式( src/tests/docs/.github/ )的路径会被拒绝,但少量顶层项目文件允许列表(README.mdAGENTS.mdmkdocs.yml)除外;这些文件将根据 仓库相对 路径进行匹配。以仓库根目录为锚点可以防止 /etc/README.md 仅凭文件名匹配绕过限制。请注意, SECURITY.md 和许可证文件仍不在允许列表中。这符合“未经许可不得修改”的规则;该规则定义于 AGENTS.md

允许的操作会输出:

{"permissionDecision":"allow"}

被拒绝的操作会向 logs/security-denials.log 追加一行,并输出以下结构:

{"permissionDecision":"deny","permissionDecisionReason":"Access to credential/secret files blocked by security policy"}

audit-logger.sh

audit-logger.shpostToolUse 钩子。它读取 .toolName.toolArgs.timestamp.cwd,将字符串化的工具参数截断为 500 个字符,对操作进行分类,创建 logs/,并以每行一个 JSON 对象的形式追加到 logs/agent-audit.jsonl

类别如下:

工具名称 类别
bash command-execution
edit code-edit
create file-creation
view code-read
grepglob code-search
其他任何工具 other

审计记录格式如下:

{
  "timestamp": "input timestamp",
  "logged_at": "UTC write time",
  "tool": "tool name",
  "category": "operation category",
  "args": "first 500 characters of toolArgs",
  "cwd": "working directory"
}

session-end.sh

session-end.sh 从标准输入读取 .reason ,统计 logs/agent-audit.jsonllogs/security-denials.log 中的行数(如果这些文件存在),然后追加带格式的 SESSION END 块至 logs/session.log

已修复: preToolUse 从未运行

retail-governance.json 之前将 preToolUse 指向 ./.github/hooks/scripts/security-gate-notworking.sh,但该文件在磁盘上不存在。实际脚本是 security-gate.sh,因此该门禁一直未执行且没有任何提示。现已修正。

由此可见,钩子配置错误可能会静默失败。请务必验证门禁是否确实触发,尤其是用于实施安全控制的拒绝路径。

手动测试钩子

在仓库根目录中,将具有代表性的钩子 JSON 通过管道传给脚本,即可直接运行该脚本:

echo '{"toolName":"bash","toolArgs":{"command":"cat .env"}}' | \
  ./.github/hooks/scripts/security-gate.sh

预期结果:输出一个拒绝操作的 JSON 对象,并在 logs/security-denials.log 中新增一行。

可以用相同方式测试允许的路径:

echo '{"toolName":"create","toolArgs":{"path":"docs/example.md"}}' | \
  ./.github/hooks/scripts/security-gate.sh

临时禁用钩子

进行短期本地实验时,可以删除相关事件条目,或将该事件数组设置为 [];请在 .github/hooks/retail-governance.json 中完成此修改,并在提交前恢复。应尽量只禁用必要的最小范围钩子;例如,测试安全门禁配置时仅清空 preToolUse 即可。