global-agent-guardrails

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Global Agent Guardrails

全局Agent防护机制

A "bouncer" that blocks catastrophic shell commands before any AI agent runs them. One patterns file is the single source of truth; every agent reads it via a shared hook script or a tiny native adapter. It is a seatbelt against accidents, NOT a sandbox against a malicious agent (obfuscation like
python -c "shutil.rmtree(...)"
can slip past regex).
这是一个“守门人”,能在任何AI Agent执行命令前拦截灾难性Shell命令。一份规则文件是唯一的可信数据源;所有Agent通过共享钩子脚本或轻量原生适配器读取它。它是防止意外的“安全带”,而非针对恶意Agent的沙箱(像
python -c "shutil.rmtree(...)"
这类混淆写法可以绕过正则检测)。

File map

文件结构

~/.agents/hooks/dangerous-patterns.txt   # THE denylist: one POSIX-ERE regex per line, # comments
~/.agents/hooks/deny-dangerous.sh        # shared guard: hook JSON on stdin -> exit 2 blocks
~/.agents/hooks/test-guard.sh            # test suite: run after ANY pattern change
~/.config/opencode/plugins/command-guard.ts   # OpenCode adapter (throws to block)
~/.pi/agent/extensions/command-guard.ts       # Pi adapter (returns {block:true})
~/.hermes/plugins/command-guard/              # Hermes plugin (returns {"action":"block"})
~/.agents/hooks/dangerous-patterns.txt   # 黑名单:每行一个POSIX-ERE正则,#开头为注释
~/.agents/hooks/deny-dangerous.sh        # 共享防护脚本:从标准输入读取JSON,返回码2表示拦截
~/.agents/hooks/test-guard.sh            # 测试套件:任何规则变更后都需运行
~/.config/opencode/plugins/command-guard.ts   # OpenCode适配器(抛出异常实现拦截)
~/.pi/agent/extensions/command-guard.ts       # Pi适配器(返回{block:true})
~/.hermes/plugins/command-guard/              # Hermes插件(返回{"action":"block"})

State check (is it installed?)

状态检查(是否已安装?)

bash
ls ~/.agents/hooks/deny-dangerous.sh ~/.agents/hooks/dangerous-patterns.txt
~/.agents/hooks/test-guard.sh   # must end "failed: 0"
If missing, rebuild from the wiring table below.
bash
ls ~/.agents/hooks/deny-dangerous.sh ~/.agents/hooks/dangerous-patterns.txt
~/.agents/hooks/test-guard.sh   # 必须以"failed: 0"结尾
如果文件缺失,请根据下方的集成表重新构建。

Add or tune a pattern

添加或调整规则

  1. Edit
    ~/.agents/hooks/dangerous-patterns.txt
    . Write POSIX ERE (
    grep -E
    ). Use
    [[:space:]]
    , never
    \s
    — adapters auto-convert
    [:space:]
    to
    \s
    for JS/Python and compile in multiline mode.
  2. Add block + allow cases to
    test-guard.sh
    , then run it. Must pass 100%.
  3. Verify the new pattern compiles in the adapter engines:
bash
python3 -c 'import re,pathlib; [re.compile(l.strip().replace("[:space:]",r"\s"),re.M) for l in pathlib.Path.home().joinpath(".agents/hooks/dangerous-patterns.txt").read_text().splitlines() if l.strip() and not l.startswith("#")]; print("ok")'
  1. Changes apply instantly everywhere (all consumers re-read the file per command). Exception: Droid uses its own
    commandBlocklist
    in
    ~/.factory/settings.json
    — mirror the change there manually.
Design rule: block only irreversible/catastrophic commands (data loss, disk wipe, repo deletion, token exfil). Local-destructive-but-recoverable commands (
git status
,
git clean -fdx
,
rm -rf node_modules
) stay ALLOWED — over-blocking kills agent usefulness.
Password managers are also a hard NO (pattern group 10): agents must never use their CLIs (
bw
,
bws
,
lpass
,
keepassxc-cli
,
rbw
,
nordpass
outright;
pass
with any argument at command position;
op
with its real subcommands — bare
op
/
pass
stay unblocked because they are common words), dump the macOS keychain (
security find-*-password
,
dump-keychain
), export gpg secret keys, touch vault data (
~/.password-store
, the
.app
bundles), or open/uninstall the apps.
  1. 编辑
    ~/.agents/hooks/dangerous-patterns.txt
    。使用POSIX ERE正则(即
    grep -E
    兼容的语法)。使用
    [[:space:]]
    表示空格,绝对不要用
    \s
    ——适配器会自动将
    [:space:]
    转换为
    \s
    以适配JS/Python,并以多行模式编译。
  2. test-guard.sh
    中添加拦截和允许的测试用例,然后运行脚本。必须100%通过。
  3. 验证新规则能在适配器引擎中正常编译:
bash
python3 -c 'import re,pathlib; [re.compile(l.strip().replace("[:space:]",r"\s"),re.M) for l in pathlib.Path.home().joinpath(".agents/hooks/dangerous-patterns.txt").read_text().splitlines() if l.strip() and not l.startswith("#")]; print("ok")'
  1. 变更会立即在所有Agent中生效(所有消费者每次执行命令时都会重新读取规则文件)。例外情况:Droid使用
    ~/.factory/settings.json
    中自带的
    commandBlocklist
    ,需手动同步规则变更。
设计原则:仅拦截不可逆/灾难性命令(数据丢失、磁盘擦除、仓库删除、令牌泄露)。本地破坏性但可恢复的命令(如
git status
git clean -fdx
rm -rf node_modules
)保持允许——过度拦截会降低Agent的实用性。
密码管理器同样是严格禁止的(规则组10):Agent绝不能使用它们的CLI(直接拦截
bw
bws
lpass
keepassxc-cli
rbw
nordpass
;拦截带任意参数的
pass
命令;拦截带真实子命令的
op
——仅输入
op
/
pass
则不拦截,因为它们是常用词汇),禁止导出macOS钥匙串(
security find-*-password
dump-keychain
)、导出GPG私钥、触碰密码库数据(
~/.password-store
、相关
.app
包),或打开/卸载这些应用。

Per-agent wiring (user-global)

按Agent集成(全局用户级)

AgentConfigEventBlocks via
Claude Code
~/.claude/settings.json
PreToolUse
matcher
Bash
shared script, exit 2
Codex CLI/app/IDE
~/.codex/hooks.json
PreToolUse
matcher
Bash
shared script, exit 2
Cursor IDE + CLI
~/.cursor/hooks.json
beforeShellExecution
shared script with
cursor
arg, deny JSON
Grok (xAI)auto-loads Claude + Cursor hook files (compat on by default); native option
~/.grok/hooks/*.json
PreToolUse
shared script (reads
.toolInput.command
)
OpenCode
~/.config/opencode/plugins/command-guard.ts
tool.execute.before
adapter throws Error
Pi
~/.pi/agent/extensions/command-guard.ts
pi.on("tool_call")
adapter returns
{block:true}
Hermes
~/.hermes/plugins/command-guard/
(
plugin.yaml
+
__init__.py
)
pre_tool_call
hook
plugin returns
{"action":"block"}
Droid (Factory)
~/.factory/settings.json
native
commandBlocklist
hard-block, no approval possible
Devin CLI
~/.config/devin/config.json
PreToolUse
matcher
^exec$
shared script, exit 2
Hook entry shape for Claude/Codex/Devin (merge into existing
hooks
object, never overwrite):
json
{"hooks": {"PreToolUse": [{"matcher": "Bash", "hooks": [{"type": "command", "command": "/ABSOLUTE/HOME/.agents/hooks/deny-dangerous.sh"}]}]}}
Cursor entry (payload has
.command
, so pass the
cursor
arg):
json
{"beforeShellExecution": [{"command": "/ABSOLUTE/HOME/.agents/hooks/deny-dangerous.sh cursor", "failClosed": false}]}
Use absolute paths in configs (
~
expansion is inconsistent across agents).
Agent配置文件触发事件拦截方式
Claude Code
~/.claude/settings.json
PreToolUse
匹配器
Bash
共享脚本,返回码2
Codex CLI/app/IDE
~/.codex/hooks.json
PreToolUse
匹配器
Bash
共享脚本,返回码2
Cursor IDE + CLI
~/.cursor/hooks.json
beforeShellExecution
cursor
参数的共享脚本,返回拦截JSON
Grok (xAI)自动加载Claude + Cursor钩子文件(默认兼容开启);原生配置选项
~/.grok/hooks/*.json
PreToolUse
共享脚本(读取
.toolInput.command
OpenCode
~/.config/opencode/plugins/command-guard.ts
tool.execute.before
适配器抛出Error
Pi
~/.pi/agent/extensions/command-guard.ts
pi.on("tool_call")
适配器返回
{block:true}
Hermes
~/.hermes/plugins/command-guard/
plugin.yaml
+
__init__.py
pre_tool_call
钩子
插件返回
{"action":"block"}
Droid (Factory)
~/.factory/settings.json
原生
commandBlocklist
强制拦截,无审批可能
Devin CLI
~/.config/devin/config.json
PreToolUse
匹配器
^exec$
共享脚本,返回码2
Claude/Codex/Devin的钩子配置格式(合并到现有
hooks
对象中,请勿覆盖):
json
{"hooks": {"PreToolUse": [{"matcher": "Bash", "hooks": [{"type": "command", "command": "/ABSOLUTE/HOME/.agents/hooks/deny-dangerous.sh"}]}]}}
Cursor的配置项(负载包含
.command
,需传入
cursor
参数):
json
{"beforeShellExecution": [{"command": "/ABSOLUTE/HOME/.agents/hooks/deny-dangerous.sh cursor", "failClosed": false}]}
配置中请使用绝对路径(不同Agent对
~
的解析行为不一致)。

Gotchas (hard-won — do not rediscover)

注意事项(经验总结——请勿重复踩坑)

  • Codex trust is hash-pinned. Any edit to the hook ENTRY in
    hooks.json
    (not the patterns file) invalidates trust; run
    /hooks
    in Codex and re-trust, else Codex silently skips the guard. Trust hashes live in
    [hooks.state]
    in
    ~/.codex/config.toml
    and are shared by CLI, desktop app, and IDE extension. CI/scripts:
    --dangerously-bypass-hook-trust
    .
  • Cursor
    failClosed
    must stay
    false
    .
    Cursor background/worker hosts cannot execute hook scripts; fail-closed blocks EVERY command there. Trade-off: Cursor background agents run unguarded.
  • Hermes plugin manifest key is
    provides_hooks
    (not
    hooks
    ). Plugin must be enabled:
    plugins.enabled
    list in
    ~/.hermes/config.yaml
    (the
    hermes plugins enable
    CLI prompts interactively and hangs non-interactive shells). Hermes hooks are fail-open on exceptions — keep the plugin trivial. Shell tool name is
    terminal
    .
  • Pi
    tool_call
    handler errors block the tool
    (fail-safe) — adapter must catch its own errors and fail open, or a broken patterns file bricks every bash call.
  • Droid semantics:
    commandDenylist
    = ask for confirmation;
    commandBlocklist
    = never runs, even at full autonomy with
    --skip-permissions-unsafe
    . Use blocklist for catastrophic entries.
  • Guard script payload detection: command lives at
    .tool_input.command
    (Claude/Codex/Devin),
    .toolInput.command
    (Grok),
    .command
    (Cursor). Keep all three in the jq fallback chain.
  • Adapter regexes require multiline mode. Keep JavaScript's
    m
    flag and Python's
    re.M
    so
    ^
    matches each shell line like
    grep
    .
  • False-positive class: a harmless command whose ARGUMENT text contains a dangerous-looking string (e.g. passing a prompt mentioning
    git push --force
    on a CLI) gets blocked. Workaround: put the text in a file and reference it.
  • Not coverable natively (no hook system as of 2026-07): Gemini CLI, Qwen Code, Amp, kimi-cli. Codex cloud tasks and Cursor background agents also bypass the local guard.
  • Codex信任基于哈希固定:对
    hooks.json
    中的钩子配置项(而非规则文件)的任何修改都会使信任失效;需在Codex中运行
    /hooks
    命令重新信任,否则Codex会静默跳过防护。信任哈希存储在
    ~/.codex/config.toml
    [hooks.state]
    中,CLI、桌面应用和IDE扩展共享该哈希。CI/脚本场景可使用
    --dangerously-bypass-hook-trust
    参数。
  • Cursor的
    failClosed
    必须设为
    false
    :Cursor后台/工作进程无法执行钩子脚本;若设为fail-closed会拦截所有后台命令。权衡方案:Cursor后台Agent不启用防护。
  • Hermes插件清单的关键字是
    provides_hooks
    (而非
    hooks
    )。插件必须启用:在
    ~/.hermes/config.yaml
    plugins.enabled
    列表中添加(
    hermes plugins enable
    命令会交互式提示,在非交互式Shell中会挂起)。Hermes钩子在异常时会fail-open——请保持插件逻辑简洁。Shell工具名称为
    terminal
  • Pi的
    tool_call
    处理器错误会拦截工具
    (故障安全机制)——适配器必须捕获自身错误并fail-open,否则损坏的规则文件会导致所有bash调用失效。
  • Droid语义说明
    commandDenylist
    = 需确认后执行;
    commandBlocklist
    = 绝对不执行,即使在完全自主模式下使用
    --skip-permissions-unsafe
    也无法执行。灾难性命令需加入blocklist。
  • 防护脚本的负载检测:命令的位置为
    .tool_input.command
    (Claude/Codex/Devin)、
    .toolInput.command
    (Grok)、
    .command
    (Cursor)。请在jq回退链中保留这三种路径。
  • 适配器正则需启用多行模式:保留JavaScript的
    m
    标志和Python的
    re.M
    ,使
    ^
    能匹配Shell的每一行(与
    grep
    行为一致)。
  • 误拦截场景:无害命令的参数中包含危险字符串(例如在CLI中传入包含
    git push --force
    的提示文本)会被拦截。解决方法:将文本存入文件后引用。
  • 原生不支持防护(截至2026-07无钩子系统):Gemini CLI、Qwen Code、Amp、kimi-cli。Codex云任务和Cursor后台Agent也会绕过本地防护。

E2E verification recipe

端到端验证方法

Safe probe: ask the agent to run
git push --force
from a NON-git directory — blocked = guard works; "not a git repository" = guard failed but no harm done.
bash
cd "$(mktemp -d)"
claude -p 'Run exactly: git push --force. Report the result in one line.' --permission-mode bypassPermissions
codex exec --skip-git-repo-check 'Run exactly: git push --force. Report the result in one line.' < /dev/null
droid exec --auto high -f prompt.txt        # prompt text in a file (see false-positive gotcha)
pi -p --no-session 'Run exactly: git push --force. Report the result in one line.'
hermes chat --query 'Run exactly this terminal command: git push --force. Report in one line.'
Direct script test without any agent:
bash
echo '{"tool_input":{"command":"rm -rf /"}}' | ~/.agents/hooks/deny-dangerous.sh; echo "exit=$?"   # expect exit=2
安全测试:让Agent在非Git目录下执行
git push --force
——被拦截=防护生效;提示“not a git repository”=防护失效但无危害。
bash
cd "$(mktemp -d)"
claude -p 'Run exactly: git push --force. Report the result in one line.' --permission-mode bypassPermissions
codex exec --skip-git-repo-check 'Run exactly: git push --force. Report the result in one line.' < /dev/null
droid exec --auto high -f prompt.txt        # 提示文本存入文件(参考误拦截注意事项)
pi -p --no-session 'Run exactly: git push --force. Report the result in one line.'
hermes chat --query 'Run exactly this terminal command: git push --force. Report in one line.'
无需Agent的直接脚本测试:
bash
echo '{"tool_input":{"command":"rm -rf /"}}' | ~/.agents/hooks/deny-dangerous.sh; echo "exit=$?"   # 预期返回exit=2