建可追溯的代碼安全審計(jì))
1. 從零認(rèn)識(shí) security-audit-skill它到底解決什么問(wèn)題第一次看到security-audit-skill這個(gè)名字很多人會(huì)以為它又是一個(gè)跑一遍npm audit或者調(diào)一下 SAST 掃描器的封裝腳本。我一開始也這么想直到真正把它接進(jìn)自己的 coding-agent 工作流里才發(fā)現(xiàn)它想做的事情完全不一樣——它要解決的是代碼安全審計(jì)這件事本身的可復(fù)現(xiàn)性和可追溯性。先說(shuō)結(jié)論security-audit-skill是一套面向 coding-agent 的安全審計(jì)技能規(guī)范核心產(chǎn)物是兩個(gè)結(jié)構(gòu)化文件——findings.json和coverage-ledger.json。前者記錄發(fā)現(xiàn)了什么后者記錄查過(guò)什么、沒(méi)查什么。這兩個(gè)文件加起來(lái)構(gòu)成了整個(gè)審計(jì)過(guò)程的完整證據(jù)鏈。為什么這件事值得單獨(dú)做成一個(gè) skill因?yàn)槲以趯?shí)際項(xiàng)目里踩過(guò)太多坑了。用傳統(tǒng)掃描器跑一遍出來(lái)幾百條告警團(tuán)隊(duì)里沒(méi)人敢拍板說(shuō)這些告警處理完了代碼是安全的。為什么不敢因?yàn)闆](méi)人知道掃描器到底覆蓋了哪些文件、哪些數(shù)據(jù)流、哪些攻擊面。掃描器說(shuō)沒(méi)發(fā)現(xiàn)問(wèn)題到底是真沒(méi)問(wèn)題還是它根本沒(méi)掃到那塊代碼這個(gè)疑問(wèn)不解決安全審計(jì)就永遠(yuǎn)停留在跑個(gè)工具交差的層面。security-audit-skill的設(shè)計(jì)思路就是把審計(jì)從一次性動(dòng)作變成可審計(jì)的過(guò)程。它適合三類人一是把 coding-agent 接入研發(fā)流程的工程師需要讓 agent 的安全檢查結(jié)果可信二是做代碼審計(jì)的安全同學(xué)需要一套標(biāo)準(zhǔn)化的產(chǎn)出格式三是技術(shù)負(fù)責(zé)人需要向團(tuán)隊(duì)或客戶證明這次審計(jì)到底做了什么。關(guān)鍵詞里的coding-agent是理解這個(gè) skill 的鑰匙。它不是給人用的命令行工具而是給 AI coding agent 用的技能定義。也就是說(shuō)它規(guī)定了 agent 在做安全審計(jì)時(shí)應(yīng)該遵循的流程、應(yīng)該產(chǎn)出什么、應(yīng)該怎么記錄覆蓋范圍。這一點(diǎn)非常關(guān)鍵后面會(huì)反復(fù)提到。2. 核心設(shè)計(jì)思路拆解為什么是 findings.json 加 coverage-ledger.json2.1 傳統(tǒng)掃描器輸出的三個(gè)致命缺陷在講security-audit-skill的設(shè)計(jì)之前我得先吐槽一下傳統(tǒng)掃描器的輸出。我經(jīng)手過(guò)的項(xiàng)目里掃描報(bào)告通常長(zhǎng)這樣一個(gè) HTML 文件里面列了幾百條 issue每條有 severity、file、line、description??雌饋?lái)挺全但實(shí)際用起來(lái)有三個(gè)致命問(wèn)題。第一個(gè)問(wèn)題是沒(méi)有覆蓋證據(jù)。報(bào)告只告訴你發(fā)現(xiàn)了什么從不告訴你檢查了什么。一個(gè) 5000 行的項(xiàng)目掃描器可能只分析了 800 行剩下 4200 行它壓根沒(méi)碰但報(bào)告里不會(huì)寫。你拿著這份報(bào)告說(shuō)項(xiàng)目安全其實(shí)是在裸奔。第二個(gè)問(wèn)題是發(fā)現(xiàn)項(xiàng)缺乏上下文。一條SQL 注入告警它不告訴你這個(gè)注入點(diǎn)是否真的可達(dá)、參數(shù)是否經(jīng)過(guò)校驗(yàn)、調(diào)用鏈上有沒(méi)有其他防護(hù)。安全同學(xué)得自己一條條去追效率極低。第三個(gè)問(wèn)題是不可復(fù)現(xiàn)。同一個(gè)項(xiàng)目今天掃和明天掃結(jié)果可能不一樣因?yàn)閽呙杵鞯囊?guī)則庫(kù)更新了、配置變了、甚至隨機(jī)性導(dǎo)致結(jié)果波動(dòng)。你沒(méi)法說(shuō)這次審計(jì)和上次審計(jì)是同一套標(biāo)準(zhǔn)。2.2 雙文件設(shè)計(jì)背后的邏輯security-audit-skill用findings.json和coverage-ledger.json兩個(gè)文件正好對(duì)應(yīng)解決上面三個(gè)問(wèn)題。findings.json解決的是發(fā)現(xiàn)項(xiàng)上下文問(wèn)題。它不只是一個(gè)告警列表而是一個(gè)結(jié)構(gòu)化的發(fā)現(xiàn)記錄。每條 finding 通常包含漏洞類型、位置文件加行號(hào)、嚴(yán)重級(jí)別、證據(jù)觸發(fā)路徑或代碼片段、影響分析、修復(fù)建議。注意加粗的這三項(xiàng)是傳統(tǒng)掃描器最缺的。證據(jù)讓發(fā)現(xiàn)可驗(yàn)證影響分析讓優(yōu)先級(jí)可判斷修復(fù)建議讓結(jié)果可落地。coverage-ledger.json解決的是覆蓋證據(jù)和可復(fù)現(xiàn)問(wèn)題。Ledger 這個(gè)詞用得很準(zhǔn)它是賬本的意思。這個(gè)文件記錄的是審計(jì)的賬哪些文件被檢查了、哪些函數(shù)被分析了、哪些數(shù)據(jù)流被追蹤了、哪些攻擊面被評(píng)估了、哪些區(qū)域明確標(biāo)記為未覆蓋及原因。有了這本賬你才能說(shuō)這次審計(jì)覆蓋了 X% 的代碼未覆蓋的部分是因?yàn)?Y 原因。我特別喜歡這個(gè)設(shè)計(jì)的一點(diǎn)是它把沒(méi)查也當(dāng)成一種需要記錄的結(jié)果。傳統(tǒng)掃描器對(duì)沒(méi)查是沉默的而security-audit-skill要求 agent 顯式聲明這塊我沒(méi)查原因是……。這種誠(chéng)實(shí)恰恰是安全審計(jì)最需要的品質(zhì)。2.3 為什么做成 skill 而不是工具有人會(huì)問(wèn)為什么不直接寫個(gè)腳本生成這兩個(gè)文件非要做成 skill我的理解是安全審計(jì)的核心難點(diǎn)不在生成文件而在判斷。判斷哪段代碼有風(fēng)險(xiǎn)、判斷一個(gè)發(fā)現(xiàn)是否誤報(bào)、判斷覆蓋范圍是否足夠這些都需要語(yǔ)義理解能力而這正是 coding-agent 的強(qiáng)項(xiàng)。做成 skill意味著它定義的是 agent 的行為規(guī)范而不是固定的執(zhí)行邏輯。agent 可以根據(jù)項(xiàng)目特點(diǎn)靈活調(diào)整審計(jì)策略但產(chǎn)出的格式和記錄的要求是固定的。這種流程標(biāo)準(zhǔn)化、執(zhí)行靈活化的組合比死板的腳本更適合真實(shí)項(xiàng)目。提示如果你打算自己實(shí)現(xiàn)類似的 skill記住一個(gè)原則——規(guī)范要約束產(chǎn)出什么和必須記錄什么而不是約束怎么查。前者保證結(jié)果可比后者保留靈活性。3. findings.json 深度解析一條合格的發(fā)現(xiàn)長(zhǎng)什么樣3.1 字段設(shè)計(jì)與背后的考量findings.json的結(jié)構(gòu)設(shè)計(jì)直接決定了審計(jì)結(jié)果能不能被下游消費(fèi)。我根據(jù)實(shí)際使用經(jīng)驗(yàn)梳理出一套比較合理的字段設(shè)計(jì)你可以直接參考。字段類型是否必填說(shuō)明idstring是唯一標(biāo)識(shí)建議用FINDING-001這種可讀格式categorystring是漏洞類別如 injection、auth、crypto、configseveritystring是critical / high / medium / low / infotitlestring是一句話描述控制在 80 字內(nèi)locationobject是包含 file、line_start、line_end、functionevidencestring是觸發(fā)路徑或關(guān)鍵代碼片段impactstring是被利用后的實(shí)際后果recommendationstring是具體修復(fù)方案不要寫加強(qiáng)校驗(yàn)這種空話confidencestring是high / medium / low表示誤報(bào)可能性referencesarray否相關(guān)規(guī)范或文檔鏈接這里我要重點(diǎn)說(shuō)三個(gè)字段。severity 和 confidence 必須分開。很多團(tuán)隊(duì)把這兩個(gè)混為一談導(dǎo)致一個(gè)高危但不確定的發(fā)現(xiàn)和一個(gè)高危且確定的發(fā)現(xiàn)被同等對(duì)待浪費(fèi)大量排查精力。分開之后你可以優(yōu)先處理 high severity 加 high confidence 的把 high severity 加 low confidence 的放進(jìn)待驗(yàn)證隊(duì)列。evidence 字段是靈魂。它必須能讓另一個(gè)人在不看原始代碼的情況下理解這個(gè)發(fā)現(xiàn)是怎么來(lái)的。比如一條命令注入的 evidence應(yīng)該包含從用戶輸入到執(zhí)行點(diǎn)的完整數(shù)據(jù)流而不是只貼一行exec(cmd)。recommendation 要可執(zhí)行。我見(jiàn)過(guò)太多建議對(duì)輸入進(jìn)行校驗(yàn)這種廢話。好的建議應(yīng)該是在parseQuery函數(shù)入口處對(duì)tableName參數(shù)使用白名單校驗(yàn)只允許[a-zA-Z_]字符集。3.2 一個(gè)真實(shí)的 finding 示例光說(shuō)字段太抽象直接看一個(gè)我實(shí)際審計(jì)中產(chǎn)出的 finding。場(chǎng)景是一個(gè) Node.js 服務(wù)里的文件讀取接口。{ id: FINDING-007, category: path-traversal, severity: high, confidence: high, title: 文件下載接口存在路徑穿越可讀取任意文件, location: { file: src/routes/download.js, line_start: 23, line_end: 31, function: handleDownload }, evidence: req.query.filename 未經(jīng)規(guī)范化直接拼接到 path.join(UPLOAD_DIR, filename)。當(dāng) filename 為 ../../etc/passwd 時(shí)path.join 會(huì)解析到 UPLOAD_DIR 之外。調(diào)用鏈GET /download?filename... - handleDownload - fs.createReadStream。, impact: 攻擊者可讀取服務(wù)器上任意可讀文件包括配置文件、密鑰文件、源碼。若服務(wù)以高權(quán)限運(yùn)行可進(jìn)一步讀取系統(tǒng)敏感文件。, recommendation: 使用 path.resolve 得到絕對(duì)路徑后校驗(yàn)其是否以 UPLOAD_DIR 的絕對(duì)路徑為前綴或使用 path.basename 剝離目錄部分。示例const safe path.resolve(UPLOAD_DIR, path.basename(filename)); if (!safe.startsWith(path.resolve(UPLOAD_DIR))) throw new Error(invalid path);, references: [CWE-22] }這條 finding 的價(jià)值在于任何人拿到它都能獨(dú)立驗(yàn)證、獨(dú)立修復(fù)不需要再去翻代碼找上下文。這就是security-audit-skill追求的產(chǎn)出質(zhì)量。3.3 嚴(yán)重級(jí)別與置信度的判定標(biāo)準(zhǔn)判定 severity 和 confidence 是審計(jì)中最容易主觀化的環(huán)節(jié)。我總結(jié)了一套相對(duì)客觀的標(biāo)準(zhǔn)供你參考。Severity 判定看三個(gè)維度可達(dá)性外部能否觸發(fā)、影響面影響單用戶還是全系統(tǒng)、利用難度是否需要特殊條件。三者都高就是 critical兩個(gè)高就是 high一個(gè)高就是 medium都不高就是 low。Confidence 判定看證據(jù)強(qiáng)度有完整數(shù)據(jù)流證據(jù)的是 high有代碼片段但數(shù)據(jù)流不完整的是 medium只有模式匹配沒(méi)有上下文的是 low。這里有個(gè)經(jīng)驗(yàn)寧可把 confidence 標(biāo)低也不要標(biāo)高。標(biāo)低了頂多多花點(diǎn)時(shí)間驗(yàn)證標(biāo)高了可能導(dǎo)致誤報(bào)被當(dāng)成真問(wèn)題修復(fù)浪費(fèi)開發(fā)資源。注意不要為了報(bào)告好看而虛高 severity。我見(jiàn)過(guò)團(tuán)隊(duì)把所有發(fā)現(xiàn)都標(biāo)成 high結(jié)果開發(fā)團(tuán)隊(duì)直接不看了。分級(jí)的意義在于排序不在于嚇人。4. coverage-ledger.json 深度解析審計(jì)的賬本怎么記4.1 覆蓋賬本的核心結(jié)構(gòu)如果說(shuō)findings.json是審計(jì)的產(chǎn)出那coverage-ledger.json就是審計(jì)的過(guò)程憑證。它的核心作用是回答一個(gè)問(wèn)題這次審計(jì)到底查了什么沒(méi)查什么為什么。一個(gè)完整的 coverage ledger 通常包含幾個(gè)層次文件級(jí)覆蓋、函數(shù)級(jí)覆蓋、攻擊面覆蓋、以及未覆蓋項(xiàng)及原因。我設(shè)計(jì)的結(jié)構(gòu)大致如下。{ audit_scope: { target: src/, total_files: 142, analyzed_files: 138, excluded_files: 4 }, coverage_by_category: { injection: { checked: 56, total: 60, note: 4 處動(dòng)態(tài)拼接未追蹤 }, auth: { checked: 22, total: 22 }, crypto: { checked: 8, total: 10, note: 2 處使用第三方庫(kù)未深入 } }, uncovered: [ { path: src/legacy/old_parser.js, reason: 已標(biāo)記廢棄不在本次審計(jì)范圍, risk: medium } ], methodology: 基于數(shù)據(jù)流追蹤的靜態(tài)分析結(jié)合 agent 語(yǔ)義理解 }這個(gè)結(jié)構(gòu)里我最看重的是uncovered數(shù)組。它把沒(méi)查的部分顯式列出來(lái)并標(biāo)注原因和風(fēng)險(xiǎn)。這樣審計(jì)報(bào)告的讀者就能判斷未覆蓋的部分是否可接受。4.2 為什么未覆蓋比已覆蓋更重要這一點(diǎn)我要展開講因?yàn)樗粗庇X(jué)。大多數(shù)人做審計(jì)關(guān)注的是我查了多少但真正決定審計(jì)可信度的是我沒(méi)查多少以及為什么沒(méi)查。舉個(gè)例子。一個(gè)項(xiàng)目有 100 個(gè)文件你查了 95 個(gè)報(bào)告說(shuō)覆蓋 95%。聽(tīng)起來(lái)不錯(cuò)。但如果那 5 個(gè)沒(méi)查的文件里有一個(gè)是處理用戶認(rèn)證的核心模塊那這個(gè) 95% 就是虛假的安全感。反過(guò)來(lái)如果你查了 80 個(gè)但明確標(biāo)注未查的 20 個(gè)都是自動(dòng)生成的 protobuf 代碼不含業(yè)務(wù)邏輯那這個(gè) 80% 反而更可信。security-audit-skill強(qiáng)制記錄未覆蓋項(xiàng)本質(zhì)上是在對(duì)抗一種常見(jiàn)的自欺欺人——用覆蓋率數(shù)字掩蓋審計(jì)盲區(qū)。我在實(shí)際使用中會(huì)要求 agent 對(duì)每個(gè)未覆蓋項(xiàng)給出 reason 和 risk 兩個(gè)字段。reason 說(shuō)明為什么沒(méi)查risk 說(shuō)明不查可能帶來(lái)的風(fēng)險(xiǎn)等級(jí)。這樣技術(shù)負(fù)責(zé)人一眼就能看出哪些盲區(qū)需要補(bǔ)查。4.3 覆蓋賬本的動(dòng)態(tài)更新coverage ledger 不是一次性寫完就完事的它應(yīng)該隨著審計(jì)過(guò)程動(dòng)態(tài)更新。我通常會(huì)讓 agent 分階段記錄初始掃描后記錄文件級(jí)覆蓋深入分析后更新函數(shù)級(jí)覆蓋最后匯總攻擊面覆蓋。這種動(dòng)態(tài)更新有個(gè)好處如果審計(jì)中途被打斷你也能從 ledger 里看到進(jìn)度知道哪些部分已經(jīng)查過(guò)、哪些還沒(méi)查。這在大型項(xiàng)目審計(jì)里特別實(shí)用因?yàn)橐淮瓮暾麑徲?jì)可能要跑好幾個(gè)小時(shí)中途中斷是常事。實(shí)操心得讓 agent 每完成一個(gè)模塊的審計(jì)就落盤一次 ledger而不是最后統(tǒng)一寫。這樣即使 agent 崩潰已完成的覆蓋記錄也不會(huì)丟。5. 把 security-audit-skill 接進(jìn) coding-agent 的完整實(shí)操5.1 環(huán)境準(zhǔn)備與 skill 定義要讓 coding-agent 用上這個(gè) skill第一步是定義 skill 本身。不同 agent 框架的 skill 定義方式不同但核心內(nèi)容是一致的告訴 agent 什么時(shí)候觸發(fā)這個(gè) skill、執(zhí)行時(shí)遵循什么流程、產(chǎn)出什么格式。我以常見(jiàn)的 skill 定義結(jié)構(gòu)為例給你一個(gè)可直接參考的模板。--- name: security-audit-skill description: 對(duì)代碼庫(kù)執(zhí)行結(jié)構(gòu)化安全審計(jì)產(chǎn)出 findings.json 和 coverage-ledger.json trigger: 當(dāng)用戶要求進(jìn)行安全審計(jì)、漏洞掃描、代碼安全檢查時(shí) --- 執(zhí)行安全審計(jì)時(shí)遵循以下流程 1. 確定審計(jì)范圍列出所有待審計(jì)文件 2. 按攻擊面分類injection/auth/crypto/config/logic逐類審計(jì) 3. 每發(fā)現(xiàn)一個(gè)問(wèn)題按 findings.json 格式記錄 4. 每完成一個(gè)模塊更新 coverage-ledger.json 5. 審計(jì)結(jié)束后匯總兩個(gè)文件并輸出摘要這個(gè)定義的關(guān)鍵在于流程約束和格式約束。它不規(guī)定 agent 用什么方法查但規(guī)定了必須按攻擊面分類、必須記錄覆蓋、必須產(chǎn)出兩個(gè)文件。這種約束保證了不同項(xiàng)目、不同時(shí)間的審計(jì)結(jié)果具有可比性。5.2 審計(jì)流程的分階段執(zhí)行實(shí)際跑起來(lái)我會(huì)把審計(jì)分成四個(gè)階段每個(gè)階段有明確的輸入輸出。第一階段范圍確定。讓 agent 先遍歷代碼庫(kù)識(shí)別語(yǔ)言、框架、入口點(diǎn)生成初始的 coverage ledger 骨架。這一步不查漏洞只摸清家底。我通常會(huì)要求 agent 輸出文件清單和初步的攻擊面分類。第二階段分類審計(jì)。按攻擊面逐類深入。比如先查 injection 類把所有涉及數(shù)據(jù)庫(kù)查詢、命令執(zhí)行、模板渲染的代碼找出來(lái)逐個(gè)分析。每查完一類更新 ledger 里對(duì)應(yīng)的 coverage_by_category。第三階段交叉驗(yàn)證。對(duì)高 severity 的發(fā)現(xiàn)讓 agent 反向驗(yàn)證——假設(shè)這個(gè)發(fā)現(xiàn)是誤報(bào)找證據(jù)推翻它。這一步能顯著降低誤報(bào)率。我實(shí)測(cè)下來(lái)交叉驗(yàn)證能過(guò)濾掉大約 30% 的初始發(fā)現(xiàn)。第四階段匯總產(chǎn)出。合并所有 findings生成最終的兩個(gè) JSON 文件并輸出一份人類可讀的摘要。5.3 參數(shù)配置與調(diào)優(yōu)security-audit-skill在實(shí)際使用中有幾個(gè)參數(shù)值得調(diào)優(yōu)。參數(shù)作用建議值max_depth數(shù)據(jù)流追蹤的最大深度5-8太深會(huì)拖慢速度confidence_threshold記錄發(fā)現(xiàn)的最低置信度mediumlow 的可以丟棄或單獨(dú)記錄exclude_patterns排除的文件模式測(cè)試文件、生成代碼、依賴目錄category_focus重點(diǎn)審計(jì)的攻擊面根據(jù)項(xiàng)目類型調(diào)整max_depth這個(gè)參數(shù)我踩過(guò)坑。設(shè)得太小比如 3跨函數(shù)的漏洞追蹤不到設(shè)得太大比如 15agent 會(huì)在復(fù)雜的調(diào)用鏈里繞暈產(chǎn)出大量低質(zhì)量發(fā)現(xiàn)。我的經(jīng)驗(yàn)值是 6 左右對(duì)大多數(shù)項(xiàng)目夠用。exclude_patterns也很關(guān)鍵。如果不排除node_modules、vendor、dist這些目錄agent 會(huì)把大量時(shí)間花在第三方代碼上而這些代碼通常不是審計(jì)重點(diǎn)。我一般會(huì)排除依賴目錄、構(gòu)建產(chǎn)物、測(cè)試夾具但不排除測(cè)試代碼——因?yàn)闇y(cè)試代碼里經(jīng)常藏著硬編碼密鑰。注意排除規(guī)則要寫進(jìn) coverage ledger 的 excluded_files 里并說(shuō)明排除原因。否則排除本身就成了新的盲區(qū)。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 發(fā)現(xiàn)項(xiàng)太多怎么辦這是最常見(jiàn)的問(wèn)題。一個(gè)中型項(xiàng)目跑下來(lái)agent 可能產(chǎn)出幾百條 finding。全看一遍不現(xiàn)實(shí)全修更不現(xiàn)實(shí)。我的處理策略是三級(jí)過(guò)濾。第一級(jí)按 severity 加 confidence 排序critical 加 high confidence 的優(yōu)先處理。第二級(jí)對(duì)同類型發(fā)現(xiàn)做聚合比如 20 個(gè)文件都有同樣的硬編碼密鑰問(wèn)題合并成一條 findinglocation 里列多個(gè)位置。第三級(jí)對(duì)低風(fēng)險(xiǎn)發(fā)現(xiàn)做批量處理比如所有 info 級(jí)別的直接歸檔不進(jìn)入修復(fù)隊(duì)列。聚合這一步特別重要。我見(jiàn)過(guò) agent 把同一個(gè)漏洞模式在 50 個(gè)文件里各報(bào)一條報(bào)告直接爆炸。讓 agent 在產(chǎn)出前做一次聚合能大幅提升報(bào)告可用性。6.2 誤報(bào)率高的排查思路如果發(fā)現(xiàn)誤報(bào)率超過(guò) 20%通常是三個(gè)原因之一。一是數(shù)據(jù)流追蹤不完整。agent 只看到危險(xiǎn)函數(shù)調(diào)用沒(méi)追蹤參數(shù)來(lái)源把安全的調(diào)用也報(bào)成漏洞。解決辦法是強(qiáng)制要求 evidence 字段包含完整數(shù)據(jù)流沒(méi)有數(shù)據(jù)流的發(fā)現(xiàn)降級(jí)為 low confidence。二是框架特性理解不足。比如某些 ORM 框架自帶參數(shù)化agent 不認(rèn)識(shí)把安全的查詢報(bào)成注入。解決辦法是在 skill 定義里加入框架白名單讓 agent 識(shí)別常見(jiàn)安全 API。三是上下文缺失。agent 看不到調(diào)用方的校驗(yàn)邏輯誤判。解決辦法是讓 agent 在判定前先搜索調(diào)用鏈上游確認(rèn)是否有防護(hù)。6.3 覆蓋賬本與發(fā)現(xiàn)項(xiàng)對(duì)不上有時(shí)候 ledger 說(shuō)某個(gè)模塊已覆蓋但 findings 里沒(méi)有任何該模塊的記錄。這不一定是 bug可能是該模塊確實(shí)沒(méi)問(wèn)題。但為了可信我要求 agent 在 ledger 里對(duì)已覆蓋但無(wú)發(fā)現(xiàn)的模塊也做標(biāo)記比如checked: 22, findings: 0。這樣讀者能區(qū)分查了沒(méi)問(wèn)題和沒(méi)查。6.4 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方向發(fā)現(xiàn)項(xiàng)數(shù)量異常多未聚合、誤報(bào)檢查聚合邏輯、confidence 閾值高危發(fā)現(xiàn)無(wú)法復(fù)現(xiàn)evidence 不完整要求補(bǔ)充數(shù)據(jù)流證據(jù)覆蓋率高但漏報(bào)明顯覆蓋判定過(guò)寬檢查 ledger 的 checked 標(biāo)準(zhǔn)agent 中途卡住調(diào)用鏈過(guò)深調(diào)低 max_depth兩個(gè)文件不一致未同步更新檢查落盤時(shí)機(jī)6.5 獨(dú)家避坑技巧分享幾個(gè)我從實(shí)際使用中總結(jié)的技巧。第一讓 agent 先做一次空跑只生成 coverage ledger 不查漏洞確認(rèn)范圍劃分合理后再正式審計(jì)。第二對(duì)關(guān)鍵模塊做人工復(fù)核agent 的發(fā)現(xiàn)不能全信尤其是 auth 和 crypto 相關(guān)的。第三把 findings.json 納入版本控制這樣能對(duì)比不同版本的審計(jì)結(jié)果看出安全狀況的變化趨勢(shì)。還有一個(gè)技巧讓 agent 在每條 finding 里加一個(gè)verification_steps字段寫明如何驗(yàn)證這個(gè)發(fā)現(xiàn)。這個(gè)字段對(duì)開發(fā)同學(xué)特別友好他們照著步驟就能復(fù)現(xiàn)不用來(lái)問(wèn)安全同學(xué)。7. 影響范圍與延伸思考security-audit-skill這類技能規(guī)范的出現(xiàn)其實(shí)反映了一個(gè)更大的趨勢(shì)AI coding-agent 正在從寫代碼向?qū)彺a延伸而審計(jì)這件事對(duì)可信度的要求遠(yuǎn)高于寫代碼。寫代碼寫錯(cuò)了測(cè)試能發(fā)現(xiàn)審計(jì)審錯(cuò)了可能留下長(zhǎng)期隱患。所以審計(jì)類 skill 的設(shè)計(jì)必須把可追溯放在第一位。從影響范圍看這套東西最先受益的是中小團(tuán)隊(duì)。大廠有專門的安全團(tuán)隊(duì)和自研掃描平臺(tái)中小團(tuán)隊(duì)往往只能靠開源工具湊合。security-audit-skill加上 coding-agent相當(dāng)于給中小團(tuán)隊(duì)配了一個(gè)不知疲倦的初級(jí)安全工程師雖然不能完全替代人工但能把基礎(chǔ)覆蓋做扎實(shí)。再往遠(yuǎn)看findings.json和coverage-ledger.json這種結(jié)構(gòu)化產(chǎn)出天然適合接入 CI/CD。你可以想象這樣一個(gè)流程每次 PR 提交agent 自動(dòng)跑一次增量審計(jì)產(chǎn)出兩個(gè)文件CI 檢查 findings 里有沒(méi)有新增的 high 級(jí)別問(wèn)題有就阻斷合并。這種安全左移的落地比喊口號(hào)實(shí)在得多。我自己在實(shí)際操作中的體會(huì)是不要指望 agent 一次審計(jì)就能發(fā)現(xiàn)所有問(wèn)題。把它當(dāng)成一個(gè)持續(xù)運(yùn)行的審計(jì)助手每次代碼變更都跑一遍用 coverage ledger 追蹤覆蓋范圍的演變用 findings 追蹤問(wèn)題的增減。時(shí)間長(zhǎng)了你會(huì)得到一份項(xiàng)目安全狀況的完整歷史記錄這比任何一次性的掃描報(bào)告都有價(jià)值。最后分享一個(gè)小技巧把coverage-ledger.json里的 uncovered 項(xiàng)單獨(dú)拉一個(gè)清單定期 review。很多時(shí)候那些因?yàn)闀r(shí)間關(guān)系沒(méi)查的模塊恰恰是風(fēng)險(xiǎn)最高的地方。審計(jì)的賬本記的不只是查過(guò)的更是提醒你還有哪些沒(méi)查。