CodeQL 分析
支援語言:Python、JavaScript/TypeScript、Go、Java/Kotlin、C/C++、C#、Ruby、Swift。
技能資源: 參考檔案和範本位於 {baseDir}/references/ 和 {baseDir}/workflows/。
基本原則
-
資料庫品質不容妥協。 能夠建置的資料庫不一定是好的——快取建置不會提取任何內容,卻會報告成功。
-
資料擴充能捕捉 CodeQL 遺漏的項目。 Django、Spring 和 Express 專案仍然會將資料庫呼叫、請求解析和 Shell 執行包裝在專案特定的 API 中,而這些 API 沒有在任何出貨的模型中涵蓋。
-
明確的規則集參考可防止查詢被靜默丟棄。 切勿將套件名稱傳遞給
codeql database analyze——每個套件的defaultSuiteFile會套用隱藏過濾器,可能產生零個結果。務必產生一個.qls。 -
零發現需要調查,而非慶祝。 這可能表示提取不良、缺少模型、套件錯誤或規則集過濾。在建置後執行
{baseDir}/scripts/check_db_quality.py,確認{baseDir}/scripts/verify_query_suite.py針對使用的規則集退出碼為零——產生腳本會執行它,因此僅在重複使用或手動編輯規則集時才手動呼叫——並在報告中說明兩者均已通過。 -
macOS Apple Silicon 需要針對編譯語言採用解決方案。 退出碼 137 是
arm64e/arm64不符,並非建置失敗。在降級使用build-mode=none之前,請先嘗試 Homebrew arm64 工具或 Rosetta。 -
逐步遵循工作流程。 每個階段都為下一個階段設下閘門;跳過品質評估或資料擴充會使差距在結果中隱而不顯。
每次 Bash 呼叫都是一個全新的 Shell
任何內容都無法跨 Bash 呼叫保留:包括變數、陣列或從 build_log.sh 取得的函式。下方每個使用某個值的區塊都必須在同一個區塊中重新建立它。工作流程會指向此處而非重複說明;它們所陳述的是該位置特定的損害,因為每個損害都以不同且靜默的方式失敗:
- 遺失的函式會使
run_logged退出碼為 127,建置階梯會將其解讀為失敗的方法,並向下走到--build-mode=none,從未呼叫 CodeQL - 遺失的陣列會展開為空,因此使用者選擇的每個
--threat-model和--model-packs都會被丟棄,而最終報告仍會列出它們為已使用 - 在
set -u下遺失的純量會以unbound variable中止區塊
輸出目錄
所有產生的檔案(資料庫、建置日誌、診斷資訊、擴充功能、結果)都存放在單一輸出目錄中。
- 如果使用者在提示中指定了輸出目錄,則將其用作
OUTPUT_DIR。 - 如果未指定,則預設為
./static_analysis_codeql_1。如果該目錄已存在,則遞增為_2、_3,以此類推。
在這兩種情況下,在寫入任何檔案之前,務必先使用 mkdir -p 建立目錄。
在執行此操作之前,請將 USER_SPECIFIED_DIR 設定為使用者提示中的文字路徑,否則保持未設定狀態以自動遞增。沒有其他內容會指派它。
# 解析輸出目錄
USER_SPECIFIED_DIR="${USER_SPECIFIED_DIR:-}" # 如果有,請在此處替換使用者的路徑
if [ -n "$USER_SPECIFIED_DIR" ]; then
OUTPUT_DIR="$USER_SPECIFIED_DIR"
else
BASE="static_analysis_codeql"
N=1
while [ -e "${BASE}_${N}" ]; do
N=$((N + 1))
done
OUTPUT_DIR="${BASE}_${N}"
fi
mkdir -p "$OUTPUT_DIR"
輸出目錄在任何工作流程執行之前僅解析一次。所有工作流程都會收到 $OUTPUT_DIR 並將其產出存放在其中:
$OUTPUT_DIR/
├── rulesets.txt # 選取的查詢套件(在第 3 步之後記錄)
├── codeql.db/ # CodeQL 資料庫(包含 codeql-database.yml 的目錄)
├── build.log # 建置日誌
├── codeql-config.yml # 排除組態(解釋型語言)
├── diagnostics/ # 診斷查詢和 CSV
├── extensions/ # 資料擴充 YAML
├── raw/ # 未過濾的分析輸出
│ ├── results.sarif
│ └── run-all.qls | important-only.qls
└── results/ # 最終結果(針對重要規則集過濾,針對全部規則集複製)
└── results.sarif
資料庫探索
CodeQL 資料庫是透過其目錄中存在 codeql-database.yml 標記檔案來識別。在搜尋現有資料庫時,務必收集所有匹配項——可能有多個來自先前執行或針對不同語言的資料庫。
探索命令。 find_databases.sh 每行列印一個資料庫路徑,過濾掉失敗建置留下的標記檔案。在同一個從中選取資料庫的區塊中建立陣列——每次 Bash 呼叫都是一個全新的 Shell,因此在此處建立的陣列在下一次呼叫時會消失,而執行會得出結論認為沒有資料庫:
# 命令替換,而非 `done < <(...)`:程序替換會丟棄腳本的退出狀態,
# 因此 "codeql 不在這個 Shell 的 PATH 上"(退出碼 2)會以空列表的形式到達,
# 並路由到 "建立一個新的資料庫",而磁碟上卻有三個好的資料庫。
if ! DB_LIST=$("{baseDir}/scripts/find_databases.sh" "${OUTPUT_DIR:-.}" .); then
echo "錯誤:資料庫探索失敗——請參閱上方訊息" >&2
exit 1
fi
FOUND_DBS=()
while IFS= read -r db; do
[ -n "$db" ] || continue
FOUND_DBS+=("$db")
done <<<"$DB_LIST"
echo "找到 ${#FOUND_DBS[@]} 個現有資料庫"
# 選擇提示所需的元資料,在此處收集,而不是在它自己的區塊中:
# FOUND_DBS 在下一次 Bash 呼叫時會消失,而對一個不再存在的陣列進行迴圈
# 不會列印任何內容,並報告成功。
for db in "${FOUND_DBS[@]}"; do
CODEQL_LANG=$(codeql resolve database --format=json -- "$db" 2>/dev/null | jq -r '.languages[0]')
CREATED=$(grep '^creationMetadata:' -A5 "$db/codeql-database.yml" 2>/dev/null | grep 'creationTime' | awk '{print $2}')
echo "$db — 語言:$CODEQL_LANG,建立時間:$CREATED"
done
切勿假設資料庫名稱為 codeql.db——透過其標記檔案來探索它。
當找到多個資料庫時: 使用 AskUserQuestion 讓使用者根據上面列印的語言和建立時間,選擇要使用哪個資料庫,或是建立一個新的資料庫。AskUserQuestion 最多接受四個選項,因此如果資料庫超過四個,請提供三個最近的資料庫加上「建立一個新的資料庫」,並在提示文字中列出其餘的資料庫。如果使用者在提示中明確說明要使用哪個資料庫或要建立一個新的資料庫,則跳過 AskUserQuestion。
快速開始
針對常見情況(「掃描此程式碼庫以尋找漏洞」):
# 驗證 CodeQL 是否已安裝。如果未安裝,請在此處停止——後續的每個命令都會
# 以資訊量較少的錯誤失敗,並且執行會浪費一個建置週期才說明原因。
if ! command -v codeql >/dev/null 2>&1; then
echo "錯誤:PATH 上找不到 codeql。使用下列其中一種方式安裝:" >&2
echo " gh extension install github/gh-codeql # 接著:gh codeql install-stub" >&2
echo " brew install --cask codeql" >&2
echo " https://github.com/github/codeql-action/releases (codeql-bundle)" >&2
exit 1
fi
# jq 用於解析下一步中的 `codeql resolve database --format=json`。沒有它,
# CODEQL_LANG 會傳回空值,並且執行會針對錯誤的語言繼續進行。
if ! command -v jq >/dev/null 2>&1; then
echo "錯誤:PATH 上找不到 jq(brew install jq / apt install jq)" >&2
exit 1
fi
# uv 用於執行兩個防護腳本和兩個規則集產生器。在此處檢查它,而不是在規則集
# 產生時檢查,後者在建置之後——否則沒有 uv 的機器會在建置整個過程中浪費時間
# 然後才失敗。
if ! command -v uv >/dev/null 2>&1; then
echo "錯誤:PATH 上找不到 uv(https://docs.astral.sh/uv/getting-started/)" >&2
exit 1
fi
codeql --version
然後使用上方輸出目錄中的區塊解析 OUTPUT_DIR——它會遵守使用者指定的目錄,而單純的自動遞增則不會。
然後執行完整管線:建置資料庫 → 建立資料擴充 → 執行分析,使用下方的工作流程。
應拒絕的合理化理由
這些捷徑會導致遺漏發現。請勿接受它們:
- 「security-extended 就夠了」 - 這是基準線。務必檢查 Trail of Bits 套件和社群套件是否可用於該語言。它們會捕捉
security-extended完全遺漏的類別。 - 「security-and-quality 是最廣泛的規則集」 -
security-and-quality排除了所有experimental/查詢路徑。對於全部規則集模式,請同時匯入security-and-quality和security-experimental。差異取決於語言,為 1–52 個查詢。 - 「資料庫建置成功了,所以它是好的」 - 能夠建置的資料庫並不表示它提取得很好。務必執行品質評估,並檢查檔案計數是否符合預期的原始碼檔案。
- 「標準框架不需要資料擴充」 - 即使 Django/Spring 應用程式也有 CodeQL 未建模的自訂包裝器。跳過擴充功能意味著遺漏漏洞。
- 「對於編譯語言,build-mode=none 就可以了」 - 它會產生嚴重不完整的分析。僅在絕對最後手段時使用。在 macOS 上,請先嘗試 arm64 工具鏈解決方案或 Rosetta。
- 「建置在 macOS 上失敗了,只用 build-mode=none 吧」 - 退出碼 137 是由
arm64e/arm64不符引起的,並非根本性的建置失敗。請參閱 macos-arm64e-workaround.md。 - 「沒有發現表示程式碼是安全的」 - 執行
check_db_quality.py和verify_query_suite.py,並報告它們已通過。沒有它們,零發現和一個未提取任何內容的資料庫會產生相同的輸出。 - 「我只執行預設規則集」 / 「我直接傳遞套件名稱就好」 - 每個套件的
defaultSuiteFile會套用隱藏過濾器,可能產生零個結果。務必使用明確的規則集參考。 - 「我會把檔案放在當前目錄中」 - 所有產生的檔案必須放入
$OUTPUT_DIR。將檔案散佈在工作目錄中會使清理變得不可能,並有覆寫先前執行的風險。 - 「使用我找到的第一個資料庫就好」 - 可能存在多個針對不同語言或來自先前執行的資料庫。當找到多個資料庫時,請向使用者呈現所有選項。僅在使用者已指定要使用哪個資料庫時才跳過提示。
- 「使用者說了『掃描』,這意味著他們想要我挑選一個資料庫」 - 「掃描」不是資料庫選擇。如果存在多個資料庫且使用者未指定名稱,請提問。
工作流程選擇
此技能有三個工作流程。一旦選擇了一個工作流程,請逐步執行,不要跳過任何階段。
這些執行時間很長。資料庫建置有四種備援方法,因此請使用任務工具來追蹤進度。根據執行情況決定哪些步驟值得追蹤。
| 工作流程 | 目的 |
|---|---|
| build-database | 使用建置方法依序建立 CodeQL 資料庫 |
| create-data-extensions | 偵測或產生專案 API 的資料擴充模型 |
| run-analysis | 選擇規則集、執行查詢、處理結果 |
無人值守建置
此插件提供 /static-analysis:codeql-build,它會端對端執行建置資料庫步驟:偵測語言和工具鏈,在階梯的各個梯級之間套用來自 build-fixes.md 的修復,並強制執行品質閘門。
/static-analysis:codeql-build {"target": "/abs/path", "lang": "cpp"}
它不會問任何問題。每個失敗的方法,以及一個已建置但低於品質閾值的資料庫,都會以狀態——no-method-succeeded 和 built-below-threshold——傳回給您,讓您在此處採取行動,因為其餘的提取器錯誤是否僅限於沒有人需要分析的程式碼,是一個執行無法做出的判斷。
當建置是冗長且不確定的部分,並且您希望將其推向結論時,請使用它。當您想對嘗試哪種方法有意見,或者當建置失敗需要即時解讀時,請手動執行 build-database.md 工作。
自動偵測邏輯
如果使用者明確指定要做什麼(例如,「建立一個資料庫」、「對 ./my-db 執行分析」),則直接執行該工作流程。如果使用者的提示已清楚表明其意圖,請勿呼叫 AskUserQuestion 來選擇資料庫——例如,「建立一個新的資料庫」、「分析 static_analysis_codeql_2 中的 codeql 資料庫」、「從頭開始執行一次完整掃描」。
針對「測試」、「掃描」、「分析」或類似詞彙的預設管線: 使用上方資料庫探索中的命令探索現有資料庫,然後決定。
| 條件 | 動作 |
|---|---|
| 未找到資料庫 | 解析新的 $OUTPUT_DIR,執行建置 → 擴充功能 → 分析(完整管線) |
| 找到一個資料庫 | 使用 AskUserQuestion:重複使用它或是建立新的? |
| 找到多個資料庫 | 使用 AskUserQuestion,最多四個選項——請參閱資料庫探索 |
| 使用者明確說明意圖 | 跳過 AskUserQuestion,直接依照他們的指示執行 |
資料庫選擇提示
當找到現有資料庫且使用者未明確指定要使用哪個時,請透過 AskUserQuestion 在標題「現有 CodeQL 資料庫」下呈現它們。使用上面收集的路徑、語言和建立時間來標記每個選項——./static_analysis_codeql_1/codeql.db (language: python, created: 2026-02-24)——並將最後一個選項設為「建立一個新的資料庫」。
選擇後:
- 如果使用者選擇了一個現有資料庫: 將
$OUTPUT_DIR設定為其上層目錄(或包含它的目錄),將$DB_NAME設定為所選路徑,然後繼續執行擴充功能 → 分析。 - 如果使用者選擇「建立新的」: 解析一個新的
$OUTPUT_DIR,執行建置 → 擴充功能 → 分析。
一般決定提示
如果提示中既不清楚資料庫也不清楚工作流程,請透過 AskUserQuestion 提供四個工作流程。
…
[為翻譯而截斷]