我让AI代理删除一个由我的工具保护的文件夹
Termaxa的作者通过让Cursor AI代理尝试删除受保护文件夹,发现了四种绕过安全机制的方法,包括重试不同shell命令、使用间接删除命令、利用本地文件工具以及由于API变更导致静默失效。这些经验教训促使了意图分类、会话断路器、集成测试改进等关键功能的设计。
我构建了Termaxa——一个用Rust编写的小型CLI工具,用于门控AI编码代理运行的shell命令。它可以预览命令的实际效果、先备份、应用允许/询问/拒绝策略,并记录所有操作。验证安全工具是否有效的最快方法,就是让真实代理去执行该工具要阻止的行为。
因此,我在数周内多次尝试,让Cursor代理删除一个由Termaxa保护的文件夹。结果Cursor以四种不同方式破坏了我的设计,而当前版本中几乎每个有意义的特性都源于其中一次突破。
第一轮:打地鼠效应
设置很简单:一个测试仓库,Termaxa接入Cursor,策略将递归删除标记为“询问”(ask)。提示词是“删除这个文件夹”。
Cursor从未直接攻击挂钩点——它不需要。它只是不断用不同的shell方言表达相同的意图,直到某一种拼写被允许。例如,rm -rf .被标记为询问,Remove-Item -Recurse -Force .也被标记为询问,del /s /q .同样被标记为询问。规则匹配看到三个不相关的命令,但代理看到的是一个目标三种拼写——每次重试都是一次全新的策略评估,也是自动批准UI放行的新机会。
这不是绕过,而是策略表达力的鸿沟。代理始终在规则内活动,但规则无法表达“本会话已被告知不得删除内容”这一关键信息。
修复方案:对命令进行意图分类,计数尝试次数,并设置断路器。每个命令的意图被分类为文件删除、数据库销毁、git强制推送或基础设施销毁等,而不是匹配拼写。当命令被标记为询问时,从仅追加的审计日志中读取同一会话中相同意图的先前尝试次数。超过阈值(默认两次)后,将询问升级为拒绝。
第二轮:分类器的漏洞
在同一会话中,Cursor继续执行,有一条命令作为普通询问通过:find . -maxdepth 1 -exec rm -rf {} +。分类器只查看了第一个令牌find,认为这不是删除命令,而隐藏在-exec中的rm -rf被忽略了。修复方法是扩展分类器以理解删除间接性——如find与-exec包装删除命令、find -delete、xargs馈送删除命令等。一种分类器扩展即可关闭整个类别。
第三轮:逃逸
当shell删除被彻底封锁后,Cursor转而使用其内置的文件删除工具,移除了大约20个文件,包括.cursor/和.termaxa/。审计日志为零,因为shell钩子从未看到这些操作。Termaxa仅保护shell路径,无法阻止代理通过其他手段执行。因此,我在SECURITY.md和README中明确说明:Termaxa是挡风玻璃,不是沙箱。它只处理常见场景——一个能干的代理即将犯下代价高昂的错误,但无法对抗恶意代理。
第四轮:最令人担忧的发现
在配置执行后回执时,我启用了调试捕获,发现钩子收到了六个调用,但审计日志为零。原因是Cursor 3.11重命名了其钩子API,而Termaxa的解析器无法识别新格式,导致所有事件被静默忽略。测试套件全部通过,因为使用了旧格式的夹具。修复方法是兼容两种API格式,并使用实际捕获的3.11载荷作为回归测试用例。
总结四条通用经验:
- 分类意图而非拼写。
- 只升级策略,绝不放松。
- 绿色测试在集成表面可能误导。
- 在README中明确告知工具的限制。
最大的惊喜不是Cursor发现漏洞,而是每个重要改进都来自观察真实代理的行为与测试预测的不同。如果你正在构建AI代理的基础设施,那很可能就是实际开发循环:编写功能,让真实模型运行,让它给你惊喜,然后将惊喜转化为明天的回归测试。