那个 hook 从未抵达模型
8 月 31 日,Token Security 在 The Hacker News 上逐一介绍了 Anthropic 为本地 Claude Code 会话新推出的 Compliance API 端点。它们是实打实的进步,而那篇文章对于记录在哪里停下讲得很精确:在工具真正执行前就触发的 hook、躺在磁盘上的文件、在会话之外启动的进程,以及一切由 Bedrock 或 Vertex 提供服务的东西。上周那只 npm 蠕虫,一辈子都活在那道缝隙里。Bromure Agentic Coding 把探针接在 VM 与外界之间的那条线上,所以记录下来的是实际发生了什么,而不是有人对模型说了什么。
一份记录只能装下有人对模型说过的话。一场代理会话里有趣的那一半,正是没有人 说出口的那一半。
今年大半时间里,一位想知道 Claude Code 在某台笔记本上到底做了什么的工程主管, 只有两个选择:读开发者的终端回滚记录,或者买一套第三方封装工具。Anthropic 的 合规端点覆盖 claude.ai 和 Claude Desktop,对 Claude Code 几乎没有着墨。
这在 8 月 11 日变了。Anthropic 为本地会话推出了三个 Compliance API 端点:一个
列出会话,一个取某个会话的元数据,第三个则从
GET /v1/compliance/apps/sessions/local/{session_id}/messages 拉出完整记录。
8 月 31 日,Token Security 在 The Hacker News 上发表了
一篇讲它们能给你什么的导览。
那篇文章由 Token Security 赞助,但它依然是我读过的、谈「一份日志的视界在哪里」 最细心的说明之一。
三种块类型,以及它们覆盖了什么
记录以三种块的形式送达。用文章自己的话说:“凡是传达给模型的内容,都会被记录
成三种块类型:text、tool_use 和 tool_result。三者合起来覆盖了用户提示、
bash 命令、读取与写入,甚至包括 MCP 命令。”
这已经很多了:模型选过的每一条 shell 命令、它读过的每一个文件、它调用过的每一个 MCP 工具。如果你的问题是“这个代理在星期二决定做了什么”,记录能回答你,而且是 从 Anthropic 的服务器回答,而不是从你正在调查的那台机器上的某个文件。
真正在干活的是第一个从句:凡是传达给模型的内容。
视界
有四样东西落在那道边界之外,文章把四样都点名了。
Hook。 “Hook 是最清楚的例子:它们在本地执行,位于模型做出决定与工具真正 执行之间,而且可以阻止某个工具执行或某个提示被发出。” Hook 是开发者磁盘上的 一段代码,在模型选定某个动作到该动作真正发生的缝隙里触发。模型从来没听说过它, 所以记录也从来不会载着它。
磁盘。 “两者都看不到磁盘上放着什么:配置文件、已安装的 skill 与插件及其 .md 文件(除非它们在某次会话中被用到),或者在会话之外启动的进程。” 一个你装好 却从没唤起过的 skill 不会留下任何痕迹,代理一小时前启动又忘掉的后台进程也一样。
其他模型提供方。 “如果你在非 Anthropic 的模型上运行 Claude Code,你完全拿 不到 Compliance API 的覆盖,因为它只记录与 Anthropic 模型的交互。跑在 Bedrock、 Foundry 或 Google Cloud 上的会话不会被覆盖。” 覆盖止于 Anthropic 的模型,于是 Codex、Gemini CLI、Grok,以及你的团队下个季度会装的任何东西,都被留在外面。
权限决定。 开发者对一条危险命令的批准,或者让整段提示直接放行的绕过标志, 都落进 OpenTelemetry,而不是合规记录。
这一切都是正确的行为。一份锚定在模型上的日志,记下的就是模型看见的东西。给你 下一次安全评审的问题是:你最需要看见的那些东西,落在这条线的模型那一侧吗?
上周那只蠕虫就住在那道缝隙里
四天前,一个每周下载量超过 15 万次的 npm 代码生成器,开始夹带凭据窃取程序。 我们 在星期六写过那条发布链: 一位陌生人在某个 pull request 上留言,项目自己的发布工作流读了那条留言、却没有 核对是谁写的,于是十个带签名的版本就发出去了。
把那份恶意代码再读一遍,另一只手上拿着合规记录。追一下每个步骤会出现在哪里。
代理执行 npm install。那是一个 tool_use 块,记录里有。之后的一切都是子进程。
第一波压根没有声明任何安装脚本。它的载荷藏在 binding.gyp 里,也就是 node-gyp
会读的构建描述文件,位于 node-gyp 会拿 Python 去求值的 conditions 字段中。
模型从来没有为它轮到过一次,也没有任何工具调用点到它的名字:一段构建文件里的
Python 表达式,以那条被记录成再平常不过的命令的孙进程身份运行。
跟着来的扫描,在主目录底下走过超过 150 个 glob 模式,寻找 SSH 密钥、.env
文件、云凭据和 registry 令牌。这一切,没有人问过模型半句。
然后是常驻。在每一个能碰到的仓库里,蠕虫写下一个 .claude/settings.json,里面
带着一个 SessionStart hook,每当有开发者在 Claude Code 里打开这个项目,就执行
setup.mjs。一个 hook、放在磁盘上、在任何人征询模型之前触发:四个盲点里的两个,
挤在同一个文件里。挑中那个位置的攻击者,几乎肯定从没读过任何厂商的合规更新日志。
整段敌意窗口持续了三小时十一分钟,其中没有任何一部分抵达过模型。
Bromure 把探针放在哪里
Bromure Agentic Coding 让每个工作区以自己的虚拟机跑在 Apple Silicon 上,离开那台 VM 的一切都会经过一个 proxy 和一个虚拟交换机,而它们住在 Hypervisor 的 Mac 那 一侧。交换机把 VM 的 80 端口和 443 端口流量导进 proxy,不需要设置任何环境变量, 客户机也没有任何东西可以关掉它。因此这份记录不依赖代理愿意配合,也不依赖代理 知不知道 proxy 在那里。
在 Activity only 这个追踪级别及以上,每一个请求都会得到一条元数据记录:
时间戳、主机、端口、方法、路径、状态码、延迟、在任何凭据替换之前量到的请求
字节数、响应字节数、proxy 在发出时替换掉了哪些凭据,以及当请求带着一个并非
Bromure 签发的 bearer 令牌时的告警。不论这个请求来自代理、来自某个 postinstall
脚本、来自某个自顾自忙碌的 MCP 服务器,还是来自某人三小时前启动后就忘掉的进程,
这条记录都存在。
Security Timeline 窗口(Window → Security Timeline…)就在旁边,回答的是另一个
问题。Trace Inspector 告诉你代理发出了什么;时间线告诉你 Bromure 的引擎决定了
什么。一张按时间排列的表格,最新的在最上面,用颜色区分:绿色代表放行,红色代表
拦截,蓝色代表信息——凭据代理、防火墙、供应链、护栏、提示注入、凭据使用、上游
TLS。它在内存里保留 5,000 行。持久副本则是
~/Library/Application Support/BromureAC/traces/ 下加密过的会话追踪文件,用与你
工作区机密相同的钥匙串密钥以 AES-GCM 封存。
同一次安装,在 Bromure 工作区里读起来是这样。
那些行里没有一行需要载荷曾经经过模型,也没有一行能被 VM 内部的代码改写,因为 写下它们的引擎跑在 Mac 上,而客户机没有任何通往它们的路径。
不管你跑的是哪个代理,都是同样那些行
一个团队统一用 Claude Code。接着有人为了某个项目带进 Codex,平台组为了数据驻留 把一个工作区导向 Bedrock,而一位研究员开始跑 Grok。按照文章自己的说法,这四个里 有三个在 Compliance API 中什么都不会产生。
一个读取 TLS 服务器名称的 proxy,用同一种方式看待这四个,而同样的 llm.request、
tool.use、command.run、file.read 与 file.write 事件会从每一个里面出来
——是从流量中抽出来的,而不是某家厂商赏给你的。在一台已加入 bromure.io 工作区的
Mac 上,它们会通过双向 TLS 流向组织,一并还有每个防火墙判定的 egress.firewall、
当某个诱饵凭据流向从未为它签发的主机时的 credential.exfiltration,以及工作区
拉取的每一个包的 supply_chain.fetch。最后这一项,就算把所有供应链强制层都关掉
也照样发出,因为 Bromure 把「看」和「拦」分开了。这道流不带原始提示。它回答的是
代理做了什么,而不是开发者问了什么。
文章收尾的那一句
全文最锋利的一句话,把覆盖范围抛在了身后:“光靠活动日志,无法告诉你一个代理的 访问是否正当。”
这对任何日志都成立,也正因如此,Bromure 的引擎自己写时间线,而不是把这件事交给 另一个旁观者。那个窗口里每一行红色,记下的都是一个已经发生的决定。一行供应链, 意味着一个代理从未收到的包。一行防火墙,意味着一条从未打开的连接。护栏那一行 是主机以 403 拒绝的推送,而代理把它读成一次普通的 API 失败;凭据代理那一行则是 一个从未变成真正机密的诱饵——因为真正的那一份从来就不在 VM 里。
策略仍然得由你来定。但你回答「这次访问正当吗」的时机,是在配置工作区的当下, 而不是几个星期后坐着读那些行。
把你的探针放在哪里
如果你在任何规模上跑编码代理,就把 Compliance API 打开。四个星期前,这个面几乎 毫无可见度,而一个星期的记录会告诉你一些关于自己团队、你原本不知道的事。
然后问第二个问题:一台记录看起来干干净净的机器,你要拿它怎么办?在 Bromure
工作区里,答案已经在屏幕上了。要看主机就打开 Window → Trace Inspector,要看
判定就打开 Window → Security Timeline,或运行 bromure-cli trace hostnames,
把那个工作区自启动以来连过的每一个域名读一遍。
代理不必提起任何一个字,它们也在那里。 安装 Bromure Agentic Coding,打开时间线,看一次安装。