Cloudflare 開源了自用的稽核器:重點是它會反駁

Cloudflare 這週發布了一個 skill,把編碼 agent 變成安全稽核員,隨後它就成了 GitHub 週榜上漲最快的儲存庫——七天大約 16,400 顆 star,總數 51,000。官方自己的說法異常克制:這只是內部漏洞挖掘流程演化出來的單一儲存庫起點,公開出來是為了讓其他人也能打造自己的一套。
值得讀的不是那份攻擊類型目錄,雖然它確實很完整。值得讀的是關於結論如何被辯倒的那一部分。
六個階段,其中一個只是記帳
這套流程是結構化的,而不是對話式的,這是它跟「叫 Claude 去找 bug」最先分開的地方。
第一階段是偵察:畫出架構、信任邊界、輸入面,還有——這一點不太常見——把它們寫進 architecture.md 和 coverage-ledger.json。第二階段依帳本把彼此隔離的獵手分派到各個單元,記錄每個人查了什麼,然後跑覆蓋度評審,找出沒有人看過的地方。第三階段把每個獨立候選交給全新的驗證者,其唯一職責是證偽它。第四階段把 confirmed、needs_validation 或 rejected 記錄寫進 findings.json,並依 JSON schema 驗證。第五階段由全新 agent 核驗最終的原始碼結論,任何實質替換都要再經過一位獨立驗證者。第六階段從已驗證的記錄推導出報告。
兩個驗證器是零依賴的純 Node 腳本:validate-coverage-ledger.cjs 在帳本建立後以及每次更新後執行,validate-findings.cjs 在第四階段執行,並在第五階段每次替換後再跑一次。它們是儲存庫裡最無趣的檔案,也是其餘部分能站得住的原因。一個產不出符合 schema 的 finding 的 agent,就不能聲稱自己找到了什麼。

三種結論才是真正的設計
多數 agent 安全工具只有兩種輸出:一串 bug,或者什麼都沒有。這個工具有三種,而中間那一檔才是思考所在。
confirmed 意味著完整的原始碼鏈路,加上一個有邊界、被觀測到的結果。needs_validation 意味著一個確切未解決的事實,且不指派嚴重等級。rejected 意味著一個被主動證偽的候選,它被記錄下來,而不是刪掉。
第三種很少見,而我認為是對的。一個被證偽的候選是關於這套程式碼的證據——它告訴下一輪不要在該處浪費時間,也告訴審查者當初考慮過什麼。多數工具把這份東西直接丟掉。
底層的設計原則在 README 裡說得很直白:只確認已經成立的邊界失效;如果一條有原始碼依據的線索卡住了,就讓它停在 needs_validation,附上那個確切未解決的事實,而不是把它升格成 finding。還有一條相關規則:縱深防禦的缺口不算漏洞——如果 A 層擋住了攻擊,B 層的缺失只是一條強化建議。
讀過機器生成的漏洞報告的人,都很清楚這些規則為什麼存在。LLM 安全審查的失效模式不是漏掉 bug,而是自信的噪音:看起來像漏洞的模式,被標成高風險,白白耗掉一個分流團隊的一週。
對抗式驗證,字面上的意思
最關鍵的一句話:檢查 finding 的 agent,永遠不是發現它的那個 agent。
這不是提示詞裡的一句叮嚀,而是流程編排層面的約束,也是這類工具能產出任何可信結果的唯一辦法。你問一個 LLM「這個 finding 對嗎」,它對自己的輸出一定會同意。給它全新的上下文、另一個 agent 實例,以及明確證偽的任務,你得到的才更接近一次程式碼審查。
第五階段把這個又推了一步:全新 agent 核驗最終的原始碼結論,如果某個結論被實質替換,再由另一位獨立驗證者檢查替換後的版本。獵手和最終留存下來的記錄之間,隔著兩層。
那個誠實的數字
設計原則裡埋著這麼一句:在 Cloudflare 的測試裡,單次執行大約能找到反覆多次執行合計發現的漏洞的一半。
公開這一點是一種選擇。它告訴你這是一台覆蓋率機器,而不是神諭,也重設了工作流程——不是在發版前跑一次,而是反覆跑,讓帳本慢慢累積。儲存庫明確支持這種用法:對同一個儲存庫的多次執行是疊加的,用先前的帳本和 findings 去定位缺口,重新驗證改動過的原始碼,並沿用目前原始碼的證據,而不是把過期的工作當成已覆蓋。
一半對於人類稽核員的首輪工作也是個合理估計。這讓人安心還是讓人絕望,取決於你怎麼看這個行業。
會擋掉大多數人的那條要求
README 裡列了一條硬性要求,多數使用者會略過它,然後一頭撞上:為建置、測試、程序、瀏覽器、模擬器、fuzzer 和 fixture 提供作業系統層級的強制沙箱,停用外部網路,使用清洗過的白名單環境,強制資源上限,寫入只允許落在暫存路徑。
沒有這些控制,工作流程會把線索停在 needs_validation,而不會去執行目標程式碼。這個判斷是對的——畢竟你是在拿語言模型的建議去跑一套程式碼庫——但這也意味著,任何沒有容器基礎設施的人,只能把它當成一場「靜態分析加推理」的練習。
這也意味著它不是筆電上的工具,儘管安裝那行寫的是 npx。安裝就一條指令:
npx skills add https://github.com/cloudflare/security-audit-skill \
--skill security-audit
用法就是開口問一句:
security audit this codebase
這裡的反差很怪:呼叫是一句話,前置條件是一整套沙箱政策。兩者之間的落差,就是大部分部署工作量要去填的地方。
輸出到底長什麼樣子
三份產物,都是推導出來的,不是手寫的:REPORT.md、FINDINGS-DETAIL.md 和 NEEDS-VALIDATION.md,再加上底層的 findings.json 與覆蓋帳本。
這個拆分比聽起來有用。單一漏洞報告會把每個發現壓進同一個語域,於是你會看到一條高風險 SQL 注入和一條缺失的 CSP 標頭並排出現。把已確認的發現、懸而未決的問題和推導出的摘要分開,意味著讀 NEEDS-VALIDATION.md 的人清楚自己看的是進行中的工作,而不是結果。
預設輸出目錄是 ~/security-audit-skill/<repo-name>/run-<N>,只有當你明確指向一個被 gitignore 的目錄時,工作流程才會寫進目標儲存庫內部。細節很小,直覺很對:儲存庫裡的稽核產物會被誤提交,而一份已經提交的未修補漏洞清單,本身就是一次事故。
怎麼判斷它到底有沒有用
跑兩遍。這是 README 暗示了卻沒明說的測試:因為執行是疊加的、單次只找到大約一半,所以對同一個 commit 的第二次執行,就是這套工具在你程式碼庫上覆蓋率的直接測量。如果第二次沒跑出新東西,要嘛你確實是乾淨的,要嘛它的獵殺類別跟你的技術堆疊對不上。
然後去看 rejected 日誌。一次執行確認了五個問題、否掉了四十個候選,說明它在做事。一次執行確認了四十個、一個都沒否掉,說明它已經停止驗證了,你得先手動把那四十個讀一遍,再決定信不信。
它對這個品類意味著什麼
三點,簡短說。
Agent skill 正在變成一種發佈形態。這不是一個你 import 的函式庫,而是一個 markdown 資料夾加兩個驗證腳本,由模型在執行時讀取。儲存庫是二十來個檔案的指令,加上依目標類型切分的攻擊類別目錄——記憶體安全與二進位、LLM 與 agent 面、Web 協定與認證、用戶端、供應鏈、雲端與部署、RPC 與訊息、資源耗盡、資料隔離、桌面與本機 IPC。這個結構本身就是產品。
驗證正在成為差異化所在。現在每個廠商都能生成一條看起來很像的 finding。但幾乎沒有誰能告訴你,哪些經過對抗式審查還站得住、寫進了 schema,而且被否掉的也留著。
而「誠實數字」的習慣正在擴散。一個敢公開「我們只找到一半」的廠商,它下一句聲明更可信。
儲存庫是 MIT 授權,這意味著有意思的問題不是它能不能用,而是你的團隊是否負擔得起它正常運作所需要的那個沙箱。
相關文章
用 LLM 代理組出一間交易公司:117,000 顆星,先讀免責聲明
分析師、看多、看空、交易員、風控、投資組合經理——組織圖就是架構,而 README 裡那句關於非決定性的提醒,才是全文最重要的一行。
225,000 顆星只為一份 markdown:Karpathy 的抱怨變成四條規則
沒有程式碼、沒有建置、沒有基準測試:四條原則寫進一份 CLAUDE.md,比今年大多數能力發布更受關注,而這個落差本身就是新聞。
Impeccable:62 條不必靠模型的規則,專門抓 AI 味
滿畫面 Inter、紫色漸層、卡片裡再塞卡片,一個給編碼代理用的設計技能用 24 個指令和 62 條確定性規則,在 CI 裡零 token 攔下這些套路。
Graphify 把資料夾變成知識圖譜,但 71.5x 需要前提
把編碼代理指向一個資料夾、輸入 /graphify,就能拿到一張邊上掛著 extracted、inferred、ambiguous 標籤的知識圖譜——標籤本身比省下的 token 更有意思。