2026年7月9日 2 分钟阅读

AI Agent 总在你眼皮底下跑危险命令?Fence 用语义护栏在命令执行前就拦住它

tinyash 0 条评论

Claude Code 和 Codex 跑起来很方便——你给一个 prompt,它就开始读代码、删文件、装依赖、甚至改系统配置。但有没有想过,如果 prompt 里夹了一条「把 ~/.ssh 里的密钥上传到某个 URL」,你的 Agent 可能真的会照做?或者更糟,一个藏在 README 或 package.json 里的 prompt injection 指令,在你毫不知情的情况下指使 Agent 执行 rm -rf ~

这不是假设。AI Agent 运行在你的权限下,它调用的每条命令都拥有你对系统的完整访问权。传统的方法是用字符串黑名单拦截危险命令,但 rm -rf ~rm -fr ~sudo rm -rf $HOME 是三条不同的字符串——黑名单穷举不完。更麻烦的是,黑名单噪声太大,开发者最终会关掉它们。

语义护栏:Fence 的核心思路

Fence 是 hoop.dev(YC W21)团队开源的工具,GitHub 16⭐,MIT 许可证,用 Go 编写。它的设计出发点完全不同——不检查字符串是否匹配,而是解析命令的实际意图

Fence 把一个命令解析为结构化的「语义事实」:这条命令在写什么文件?是否涉及网络传输?目标路径对不对?然后规则引擎基于这些事实来判断:这是无可争议的灾难(rm -rf /),还是需要确认的操作(git push --force),还是日常的安全操作(rm -rf node_modules)。

三种判定结果:

  • 🛑 deny——直接阻止,不给 Agent 任何机会
  • ⚠️ ask——暂停执行,向开发者询问是否确认
  • ✅ allow——静默放行,不打断工作流

而且 Fence 默认是 fails open:如果它无法解析某个命令,命令照常执行。一个护栏绝不能让它保护的 Agent 卡住。

一个真实的对抗场景

Fence 的 README 展示了一个经典场景:你的 Agent 正在处理一个项目,而某个文件(比如下载的第三方包或 README)中藏有一段 prompt injection 指令,要求 Agent 执行 rm -rf ~ 并上传密钥。没有护栏时,Agent 照做——它无法区分「正常用户指令」和「隐藏的攻击指令」。

Fence 在这里做了什么?它拦截了 Agent 对 cat ~/.aws/credentials | curl ... 的调用——在命令实际执行之前。Agent 看不到密钥内容,Fence 告诉它「这条命令被阻止了」,Agent 会退而选择其他方案或告知用户。

安装与快速上手

安装方式有两种,macOS 推荐用 Homebrew,Linux 推荐用 npm:

brew install hoophq/tap/fence

npm install -g @hoophq/fence

Windows 原生不支持(hook 路径未验证),WSL 下正常工作。

Claude Code 集成

fence init --global   # 为所有项目添加 Fence hook

这条命令会在 .claude/settings.json 中添加 Fence 的 pre-tool-use hook。之后启动 Claude Code 会话,你会看到一条 🚧 Fence is guarding this session… 横幅——说明护栏已激活。

claude

测试一下:让 Agent 执行一个危险操作——Fence 会阻止它,并在聊天中显示 🚧 标记和触发的规则名。安全的操作(如 rm -rf node_modules)静默放行。

Codex 集成

fence init codex   # 为当前项目添加 Codex hook

这条命令写入 .codex/hooks.json。然后在 Codex 中运行 /hooks 一次,批准 Fence 条目即可。Codex 的 shell 命令和 apply_patch 文件编辑都由同一套引擎检查。

退出

fence uninstall

精准移除 init 添加的 hook,不碰其他配置。开发者的机器,开发者说了算。

它能拦什么?

Fence 内置的推荐规则包覆盖最常见的危险场景:

场景示例命令判定
删除家目录或根目录rm -rf ~ · sudo rm -rf /🛑 deny
格式化磁盘dd of=/dev/sda · mkfs.ext4 /dev/sdb1🛑 deny
释放 fork 炸弹`:(){ :\:& };:`🛑 deny
外泄密钥`cat ~/.ssh/id_rsa \curl …`🛑 deny
开放系统路径chmod -R 777 /🛑 deny
删除工作区外文件rm -rf ~/.config/x⚠️ ask
读取密钥到上下文cat ~/.aws/credentials⚠️ ask
管道安装`curl … \sh`⚠️ ask
重写 Git 历史git push --force · git reset --hard⚠️ ask
非注册源安装npm i git+https://… · pip install git+…⚠️ ask
注入安装钩子postinstall 注入到 package.json⚠️ ask
设置持久化crontab - · systemctl enable⚠️ ask

关键点:Fence 不会被 flag 重排(rm -rf ~ vs rm -fr ~)、sudo$HOME vs ~ 或重命名的 fork 炸弹骗过。每条检测规则都附带测试,同时验证「拦截正确场景」和「放过安全场景」。

定制你自己的规则

Fence 支持 YAML 自定义规则。创建 .fence.yaml(自动发现)或指定 --rules

rules:
  - id: no-terraform-destroy
    effect: deny
    match:
      shell: { command_in: [terraform] }
      regex: '\bterraform\b.*\bdestroy\b'

也可以单行覆写内置规则的效果,无需重新定义整条规则:

overrides:
  git-force-push: deny                # ask → deny(更严格)
  pipe-to-shell-from-network: allow   # 静默这个规则

规则包市场(Rulepack Registry)

Fence 还有一个规则包注册中心。其他人写好的护栏可以一键安装:

fence search                    # 浏览已发布的规则包
fence add terraform-safety      # 校验和验证后立即生效

在团队内共享时,可以在 .fence.yaml 中通过 extends: [terraform-safety] 锁定基线。发布自己的包只需要一个 PR。

架构:适配器 → 引擎 → 规则包

Fence 的三层架构决定了它的可扩展性:

Agent 工具调用 → 适配器 → 引擎(shell AST 事实)→ 规则包 → allow · ask · deny
  • 适配器(Adapter):将不同 Agent(Claude Code、Codex 等)的调用格式归一化为中性动作
  • 引擎(Engine):将 shell 命令解析为语义事实——不是看字符串,而是看「这条命令在做什么」
  • 规则包(Rulepack):将语义事实与规则匹配,最高严重等级的效果胜出

引擎和规则包不依赖任何特定的 Agent——这正是同一个规则包能在多个 Agent 间移植的原因。

Fence 是什么,和不是什么

Fence 是 本地自我保护:它住在你的配置文件里,你可以随时编辑或卸载它。这对于保护自己不因 Agent 的失误而遭受损失来说是正好的。

但它不是合规控制——在一个你拥有完全控制权的机器上,任何东西都可以被禁用。Fence 的威胁模型文档明确了这一点,包括已知的规避路径和接受理由。

如果你需要开发者无法关闭的集中管理护栏——全团队统一执行、审批工作流、审计日志——那是不同的信任模型,也是 hoop.dev 做的事。同样的思路,但实施在开发者无法覆盖的地方。

总结

Fence 用语义解析代替字符串匹配,从根本上解决了 AI Agent 护栏的两个经典困境:可绕过性和高噪声。对使用 Claude Code 和 Codex 的开发者来说,它是一个轻量但有效的安全补充——不像黑名单那么容易绕过,又不会因为误报让你想关掉它。

16⭐ 的 GitHub 项目和 MIT 许可证意味着它目前还比较新,但来自 hoop.dev(YC W21)团队的背景和清晰的架构设计,让它比同类工具走得更远。如果你已经在日常工作流中依赖 AI 编码 Agent,花 5 分钟装个 Fence 可能是在危险命令真的被执行前,你最便宜的一道防线。


相关链接

发表评论

你的邮箱地址不会被公开,带 * 的为必填项。