← 返回部落格
Ai預計閱讀 11 分鐘

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

發布於 2026年10月28日
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 授權,這意味著有意思的問題不是它能不能用,而是你的團隊是否負擔得起它正常運作所需要的那個沙箱。

相關文章