Anthropic 在 2025 年 8 月開源了 claude-code-security-review:一個 GitHub Action,PR 一開就讓 Claude 讀那次的 diff,找出新引入的資安漏洞,然後把 finding 直接留成 PR 的行內評論。同一套分析能力也被包成 Claude Code 內建的 /security-review 斜線指令。
把 repo clone 下來逐檔讀完之後,我認為這個專案最值得看的不是那個 Action,是 claudecode/prompts.py 那 175 行。它把「怎麼叫 LLM 做資安審查而不製造雜訊」寫成了一份可以直接抄走的規格:信心門檻量化成數字、排除清單重複兩次、強制三階段方法論、輸出 schema 逼模型寫出攻擊情境。這些設計換個模型、換個實作都還成立。
所以這篇的重心放在拆那份 prompt。它的維護狀態與已知問題我也會據實寫,放在後面一節,你在決定要不要放進 CI 之前需要知道。
本文所有數字與行為描述都來自 2026 年 8 月 3 日當天的 anthropics/claude-code-security-review 主分支。
claude-code-security-review 是什麼
一句話:一個掛在 pull request 上的 GitHub Action,用 Claude 讀 PR diff 做語意層資安審查,只回報它認為高信心、真的可被利用的漏洞。
repo 基本資料:
| 項目 | 內容 |
|---|---|
| Repo | anthropics/claude-code-security-review |
| 授權 | MIT |
| 主要語言 | Python |
| 形態 | GitHub Action(composite)+ Claude Code /security-review 指令 |
| 首次公開 | 2025-08-04 |
| Star / Fork | 5,736 / 614 |
| 總 commit 數 | 30 |
| 最後 push | 2026-02-11 |
| 開啟中的 issue | 79 |
最小可用設定
README 的 Quick Start 就是全部,貼進 .github/workflows/security.yml:
name: Security Review
permissions:
pull-requests: write
contents: read
on:
pull_request:
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 2
- uses: anthropics/claude-code-security-review@main
with:
comment-pr: true
claude-api-key: ${{ secrets.CLAUDE_API_KEY }}
fetch-depth: 2 是必要的——它要拿到 diff 就得有前一個 commit。API key 必須同時開通 Claude API 和 Claude Code 用途,只開一邊會失敗。
action.yml 提供的輸入參數:
| 參數 | 預設 | 作用 |
|---|---|---|
claude-api-key | 無(必填) | API 金鑰 |
comment-pr | true | 是否在 PR 留言 |
upload-results | true | 是否上傳結果為 artifact |
exclude-directories | 空 | 逗號分隔的排除目錄 |
claude-model | 空 → claude-opus-4-1-20250805 | 分析用的模型 |
claudecode-timeout | 20 | 分析逾時(分鐘) |
run-every-commit | false | 每個 commit 都跑(跳過快取) |
false-positive-filtering-instructions | 空 | 自訂假陽性過濾規則檔路徑 |
custom-security-scan-instructions | 空 | 自訂掃描規則檔路徑 |
claudecode/constants.py 裡的預設值是 claude-opus-4-1-20250805,那是 2025 年 8 月的模型。你現在要用,claude-model 這個參數應該自己填,不要吃預設。
它的 prompt 設計:整個專案最值得抄的部分
一句話:這不是「幫我找漏洞」的一句話 prompt,是一份把「什麼該報、什麼不該報」量化成規則的審查規格。
175 行裡有四個設計值得單獨拆開講。
一、信心門檻量化成數字,不講「請謹慎判斷」
大部分人寫審查 prompt 會寫「請避免誤判」——這種話對模型幾乎沒有約束力,因為它無法對照。這份 prompt 的做法是給出可對照的刻度:Only flag issues where you're >80% confident of actual exploitability,然後要求每個 finding 自己附一個 confidence 數值,並把每個區間該對應什麼證據強度寫清楚:
- 0.9-1.0: Certain exploit path identified, tested if possible
- 0.8-0.9: Clear vulnerability pattern with known exploitation methods
- 0.7-0.8: Suspicious pattern requiring specific conditions to exploit
- Below 0.7: Don't report (too speculative)
「>80%」跟「0.7 以下不要報」是兩個不同的閘門,一個管要不要開口、一個管開口之後怎麼標。合起來的效果是:模型沒辦法用一句「可能有風險」蒙混過去,它得先把自己放到刻度上。
二、排除清單寫兩次,位置刻意分開
prompt 在開頭和結尾各寫一次「不要報什麼」——這個重複是刻意的,不是贅字。長 prompt 的中段最容易被稀釋,把約束放在頭尾兩端才守得住。
排除的項目:DOS 與資源耗盡、磁碟上的機密(另有流程處理)、rate limiting、記憶體與 CPU 耗盡、以及「沒有證明出實際問題的輸入驗證缺失」。最後一條原文很直白:
If there isn't a proven problem from a lack of input validation, don't report it.
這句話擋掉的是資安審查最大宗的雜訊來源。「這裡沒做輸入驗證」永遠成立、永遠可以報,但多數時候不構成漏洞——把它排除掉,報告才不會被淹沒。
三、三階段方法論:先讀懂專案的安全慣例,再看這次改動
這是我認為最值得抄的一段。它不是叫模型直接看 diff,而是規定順序:
| 階段 | 做什麼 |
|---|---|
| Phase 1 | 用檔案搜尋工具讀懂 repo 既有的資安框架、既有的驗證與消毒慣例、專案的威脅模型 |
| Phase 2 | 拿新程式碼跟既有慣例比對,找出偏離處與不一致的實作 |
| Phase 3 | 才逐檔評估,追資料流、找越權邊界與注入點 |
搭配的還有一條範圍限制:focus ONLY on security implications newly added by this PR. Do not comment on existing security concerns. 在 PR 上報一堆既有老問題,結果就是整個 bot 被關掉。
四、輸出 schema 逼出攻擊情境
結尾一句 Your final reply must contain the JSON and nothing else.,配上規定死的欄位:file、line、severity、category、description、exploit_scenario、recommendation、confidence。
exploit_scenario 是這個 schema 的關鍵。它逼模型寫出「攻擊者具體怎麼做」——範例給的是 Attacker could extract database contents by manipulating the 'search' parameter with SQL injection payloads like '1; DROP TABLE users--'。
這個欄位等於一道自動篩選:寫不出具體利用路徑的 finding,通常本來就不是真漏洞。與其在 prompt 裡再多叮嚀一次「請只報真的漏洞」,不如在輸出格式上要求一個寫不出來就露餡的欄位。
這四個設計加起來就是一句話:把「克制」變成可檢查的結構,而不是形容詞。 這份 prompt 就算你不用這個 Action,也值得拿去改成自己的審查指令——它是這個 repo 最能被複用的部分。
第二層:規則式的硬排除
Claude 回報完之後,claudecode/findings_filter.py 還會再過一次。裡面有一個 HardExclusionRules 類別,用預先編譯的 regex 家族做確定性過濾:
| 規則家族 | 排除什麼 |
|---|---|
_DOS_PATTERNS | 阻斷服務 |
_RATE_LIMITING_PATTERNS | 流量限制相關 |
_RESOURCE_PATTERNS | 資源耗盡 |
_OPEN_REDIRECT_PATTERNS | 開放重導 |
_MEMORY_SAFETY_PATTERNS | 記憶體安全(僅在非 C/C++ 檔時排除) |
_REGEX_INJECTION | regex 注入 |
_SSRF_PATTERNS | SSRF(僅在 .html 檔時排除) |
.c/.cc/.cpp/.h 時才丟掉——在 C/C++ 專案裡記憶體安全是真議題,不能一律過濾。SSRF 只在 .html 檔裡排除,因為純前端模板報 SSRF 基本上是誤判。另外所有 .md 檔的 finding 一律排除。
這是雙層設計:prompt 層要求模型自我克制,規則層再做一次確定性兜底。想法是對的。
第三層:AI 假陽性過濾
過完硬規則之後,還有一層要 Claude 逐條判斷 finding 是不是誤判,並附上判斷理由。README 把 "False Positive Filtering" 列為主打功能之一。
這層的一個細節值得記:單一 finding 的 API 呼叫失敗時,程式碼選擇 fail-open——保留這個 finding,給 confidence_score: 10.0,justification 寫 'Claude API failed: ...'。過濾器壞掉時寧可多報也不要漏報,這個方向是對的。
不過這層現在跑不起來,原因在下一節。
使用前要知道的維護狀態
一句話:這個 repo 目前低維護,而且第三層過濾實際上是關著的。
claudecode/claude_api_client.py 第 62 行的前置檢查把模型寫死成 claude-3-5-haiku-20241022,該模型已退役,呼叫會拿到 404。這個名稱跟你在 claude-model 參數填什麼無關,改不到。失敗被 claudecode/findings_filter.py 第 186 到 195 行接住,use_claude_filtering 設成 False,整個 AI 過濾階段跳過——Action 不會失敗、不回非零 exit code、PR 上看起來一切正常。README 描述三層過濾,實際跑起來是兩層。
這不是我一個人的推論,issue 區有四張直接影響可信度的:
| Issue | 開啟日 | 內容 |
|---|---|---|
| #114 | 2026-06-20 | 假陽性過濾呼叫已退役模型(404),過濾器靜默降級 |
| #123 | 2026-07-30 | 同一問題,指出此狀態自 2026-02-19 起持續 |
| #120 | 2026-07-20 | 快取 restore-keys 前綴比對導致新 commit 被跳過(假綠燈);可用 run-every-commit: true 繞開 |
| #118 | 2026-07-13 | action.yml 串接的 actions 仍 pin 在已棄用的 node20 |
README 自己寫明的限制
有一段 README 寫得很誠實,該引:
This action is not hardened against prompt injection attacks and should only be used to review trusted PRs.
它沒有針對 prompt injection 做防護,只該用來審查可信的 PR。官方建議去 repo 設定開啟「Require approval for all external contributors」,讓 workflow 只在維護者審過之後才跑。
這個限制在開源專案上特別要緊:任何人都能開 PR,PR 的內容會進到 prompt 裡。一個惡意的 PR 可以在程式碼註解裡塞指令,試圖讓審查器忽略某段程式碼或輸出誤導性的結論。這不是理論——這是把 LLM 接上不可信輸入的必然風險,而這個 Action 明說自己沒防。
/security-review 指令:不用架 CI 也能用
如果你只是想試試它的分析能力,不必設 GitHub Action。Claude Code 內建 /security-review 斜線指令,用的是同一套分析邏輯,直接審查你目前所有未提交的變更。
想客製化的話,README 給的做法是:把 repo 裡的 .claude/commands/security-review.md 複製到你專案的 .claude/commands/ 底下,然後改。你可以在裡面加上組織專屬的假陽性過濾規則、或補上你們特有的威脅類別。
以「花五分鐘試試值不值得」而言,這條路徑的成本遠低於架 workflow。而且——考慮到前面說的 AI 過濾層問題只發生在 Action 的 Python 管線裡——走指令這條路反而避開了那個坑。
它適合誰、不適合誰
適合: 已經在用 Claude Code、想在 PR 階段多一道語意層檢查的團隊。它的 diff-aware 設計讓成本可控,prompt 的克制程度讓它不會在 PR 上洗版,MIT 授權讓你可以隨意 fork 來改。
不適合以下情況:
你要把它當唯一的資安關卡。 三層過濾現在只有兩層在跑,加上快取那個 bug,它的「綠燈」不能當成保證。規則式 SAST 該留著。
你的 repo 接受外部 PR 且沒開審批。 README 明講沒防 prompt injection。
你需要一個積極維護的相依。 最後 push 停在 2026-02-11、79 個 issue 開著、影響核心功能的 #114 從 6 月掛到現在。要用就要有「自己 fork 來維護」的心理準備。
你不想吃預設模型。 constants.py 的預設是 claude-opus-4-1-20250805,那是 2025 年 8 月的模型。claude-model 這個參數自己填。
我的判斷
一句話:這個專案真正的資產是 prompts.py 那 175 行,Action 只是它的一種封裝。
那份 prompt 解決的是所有人用 LLM 做審查都會遇到的同一個問題——怎麼讓它別把「有可能有風險」都報出來。它的答案不是多寫幾句「請謹慎」,而是四個可檢查的結構:信心門檻量化成刻度、排除清單放在頭尾各一次、規定先讀懂專案慣例再看 diff、輸出 schema 逼出攻擊情境。這四招換到 code review、文件審查、任何要 LLM 做判斷又不能製造雜訊的場景都能用。
Python 管線那邊則示範了另一件事:寫死的模型名稱是 AI 工具最容易踩的地雷。 一行 preflight 的硬編碼,讓整個被當成賣點的過濾階段消失,而流水線照樣報綠。這跟技術難度無關,就是相依沒有跟著模型生命週期走。
實務上怎麼用:想試它的分析能力,直接開 Claude Code 跑 /security-review,成本低,而且避開了 Python 管線那個坑。要放進 CI 就先 fork,把 claude_api_client.py 第 62 行改掉,順便決定快取策略要不要繞開 #120。
不管走哪條路,它都是多一雙眼睛,不是一道門。規則式 SAST 該留著。
# 最低成本的試法:不用架 CI
claude
> /security-review