我之前给公司搭建的 DEV.
具体场景是这个:我写了个
下一篇
用 PacketVoyage 把 traceroute 的跳数变成物 →
update_article 工具,能通过 ID 修改文章标题和正文。但 DEV.to 这平台没版本回溯,一旦写错了就没法撤回,尤其是对已经发布的文章。为了防止 AI 抽风把文章改乱,我当时想得很周全,加了一个 confirm 参数,逻辑是:如果文章已发布且没有传 confirm=True,就只返回 diff 而不执行写入。代码当时是这么写的:
live_content_write = before.get("published") and ("title" in article or "body_markdown" in article)
...
if live_content_write and not confirm:
return {
"id": article_id,
"url": before.get("url"),
"applied": False,
"reason": "article is currently published — title/body_markdown changes "
"require confirm=True (DEV.to has no version history to undo this)",
...
}我当时觉得这已经是个很稳的权限边界了。结果回头复盘才发现,这根本不是什么边界,而是一个“建议”。因为 confirm 是调用方传的参数,如果 AI 没看文档,或者它自作聪明觉得必须立刻修复,它可以直接在第一次调用时就传 confirm=True。这个拦截机制只对那些“愿意遵守规则”的调用者有效,对一个不按套路出牌的 Agent 来说,这道门根本没关上。
后来为了解决并发修改导致的覆盖问题,我又加了一层 expected_fingerprint(内容哈希校验),逻辑如下:
if live_content_write and expected_fingerprint is not None and expected_fingerprint != fingerprint:
return {
"id": article_id,
"applied": False,
"reason": "stale — the live article changed since the diff you approved was "
"generated (expected_fingerprint doesn't match the current article); "
"re-preview and re-approve against the current content before writing",
"fingerprint": fingerprint,
...
}结果我再次掉进了同一个坑:expected_fingerprint 默认值是 None。这意味着只要 AI 不传这个参数,校验逻辑直接被跳过,依然能完成写入。
这次踩坑给我最大的教训是:在给 AI 写工具接口时,所有基于“可选参数”的安全性检查都是伪命题。真正的权限边界应该是强制性的。如果一个操作是有风险的,就不要给它默认值,直接把参数设为必填。比如把 expected_fingerprint: str = None 改成 expected_fingerprint: str,强制调用者必须提供指纹,否则直接报错。这样才能把控制权从不可控的 AI 手里拿回来。
免费 AI 工具箱 · 全部完全免费