/security-review 是 Claude Code 內建的指令,不用另外裝任何套件,打開終端機就有。它做的事很直接:讀目前分支跟主幹的差異,挑出新增或改動的程式碼裡有沒有資安問題。前面測過的資安工具都是裝進來的第三方套件,這次想知道的是:什麼都不裝,光靠內建這把尺,能看到什麼程度。
寫了一個很小的後端:一個附件下載端點、一個 webhook 測試端點、一個報表查詢端點。前兩個藏了真的能被打穿的洞——下載端點的檔名直接丟進 os.path.join 沒做任何檢查,可以用來讀伺服器上任意檔案;webhook 端點把使用者給的網址原封不動拿去發請求,沒有擋內網位址,是標準的 SSRF。第三個報表端點刻意留了一個比較曖昧的狀況:組 SQL 字串時用了型別提示 Literal 限制報表種類,看起來像有做防護,但 Literal 只在開發時給編輯器檢查用,執行期完全不會擋任何輸入;不過這個函式目前只把組好的 SQL 字串回傳出去,沒有接到任何資料庫執行。這條要不要算進資安漏洞報告,本身就是一個需要判斷的問題,不是單純抓得到抓不到。

直接要求審查這個分支的那邊,三個問題全找到了,連 Literal 不提供執行期防護這個細節都講對了,標成高風險問題。用內建指令的那邊,背後其實是一條小型流程:先派一個子任務去找問題,找到的每一條再分別派一個獨立的子任務去覆核是不是誤報,確認過的才寫進最終報告,不是單一次對話就直接吐結果。這條內部流程找問題的那一步,同樣精準指出了 Literal 不會在執行期擋任何東西,講法跟直接審查那邊幾乎一樣精確。

兩邊都看懂了同一個技術細節,最後的報告卻不一樣:直接審查那邊把這條列成跟另外兩個一樣的高風險項目,同時也老實註記「目前還沒有任何地方真的執行這段 SQL,所以還不成立,但這個函式本身就是一個雷」。內建指令那邊做了另一個選擇:這條被歸進「考慮過但不列入報告」,理由寫得很清楚——目前整個專案找不到任何執行 SQL 的地方,沒有資料庫驅動,這段字串目前只是被當成 JSON 回傳而已,不構成眼前可以被利用的漏洞;同時也沒有略過這件事,還是提醒了一句:以後只要有人把這段字串接去真的執行查詢,就會立刻變成真正的洞,建議現在就先改掉。

兩種做法都正確理解了這段程式碼在做什麼、為什麼有風險,差別只在怎麼決定「這個算不算現在就該報的漏洞」。一邊採取比較寬鬆的標準:只要邏輯上存在被濫用的可能,就先列出來,交給看報告的人自己判斷優先順序;一邊採取比較嚴格的標準:只把現階段真的可以被觸發的問題放進正式報告,可能影響未來的隱患另外用一段文字提醒,但不佔進漏洞清單。兩種標準都說得通,差別在於團隊比較怕漏看一個以後會爆的雷,還是比較怕報告塞滿暫時用不上的項目,導致真正急的那幾條被埋在裡面。
如果打算靠這個指令幫忙把關,值得先弄清楚它預設走的是哪一種標準,而不是看到報告裡只有兩條,就以為程式碼只有兩個問題。更實際的做法是兩段話都留著:報告裡列出來的幾條照優先順序處理,而「考慮過但不列入」那一段也要有人看過,因為那正是日後最容易被忘記、直到真的被接上危險用法才爆掉的地方。
這系列前面也測過好幾次「同一個模型,套不套技能看出來的東西差不多」,這次也是這樣,但差別出現的位置不一樣——不是在抓不抓得到,是在抓到之後,要不要讓這條訊息出現在你會看到的那份報告裡。
明天是這系列最後一天,候選清單還在累積。