ci-cd-security

ci-cd-security

掃描 GitHub Actions 工作流程檔案中的安全漏洞,直接讀取 YAML 並回報發現結果 — 無需外部工具、無需安裝、無需執行 Shell。當使用者分享 `.github/workflows/` 檔案、貼上工作流程 YAML、要求 CI/CD 安全審查、提及 `pull_request_target`、`workflow_run`、Action 固定版本、`GITHUB_TOKEN` 權限、pwn 請求、模板注入、快取中毒、機密外洩、供應鏈風險或任何 GitHub Actions 強化主題時,請使用此技能。當使用者正在強化開源儲存庫、進行 CI/CD 紅隊評估、評估供應鏈掃描目標,或公開撰寫 CI/CD 安全相關文章時,也請觸發。傾向於觸發此技能而非憑記憶回答 — CI/CD 安全預設值幾乎到處都是錯的,而且規則違反直覺。

76星標
11分支
更新於 2026/6/17
SKILL.md
唯讀
名稱
ci-cd-security
描述

掃描 GitHub Actions 工作流程檔案中的安全漏洞,直接讀取 YAML 並回報發現結果 — 無需外部工具、無需安裝、無需執行 Shell。當使用者分享 `.github/workflows/` 檔案、貼上工作流程 YAML、要求 CI/CD 安全審查、提及 `pull_request_target`、`workflow_run`、Action 固定版本、`GITHUB_TOKEN` 權限、pwn 請求、模板注入、快取中毒、機密外洩、供應鏈風險或任何 GitHub Actions 強化主題時,請使用此技能。當使用者正在強化開源儲存庫、進行 CI/CD 紅隊評估、評估供應鏈掃描目標,或公開撰寫 CI/CD 安全相關文章時,也請觸發。傾向於觸發此技能而非憑記憶回答 — CI/CD 安全預設值幾乎到處都是錯的,而且規則違反直覺。

CI/CD 安全掃描器

此技能將模型轉變為工作流程 YAML 掃描器。讀取檔案、逐一執行偵測規則、回報發現結果及其嚴重性與具體的改寫建議。無需安裝工具、無需執行指令 — 分析過程就是模型讀取 YAML。

這些規則編碼了來自 Astral、OpenSSF、GitHub Security Lab、Chainguard 以及 zizmor 審計集的當前共識。目標是標記出這些工具會標記的相同模式,而無需實際執行它們。

心智模型

每個工作流程都落在一個 2x2 矩陣中:特權 vs 無特權受信任 vs 不受信任程式碼。危害只發生在一個格子:執行不受信任程式碼的特權工作流程。以下規則是用來偵測工作流程何時落入該格子。

  • 特權 = 擁有機密、寫入權限,或產生敏感工件(發行版、部署、留言、標籤)。
  • 不受信任程式碼 = Fork PR 作者能影響的任何內容:PR 原始碼、PR 標題、PR 內文、提交訊息、分支名稱、工作流程讀取的檔案、快取、由另一個不受信任工作流程產生的工件。

若不確定某個值是否受信任,請將其視為不受信任。誤報的代價只是一個程式碼審查留言;漏報的代價則是供應鏈遭入侵。

掃描程序

針對使用者提供的每個工作流程檔案,依序執行以下掃描。每個掃描對應一類攻擊。

掃描 1:危險觸發條件

檢查 on: 區塊。立即標記:

  • pull_request_target — P0,除非有明確理由。使用機密和寫入權限執行,可由 Fork PR 觸發。這是典型的 pwn-request 向量。即使沒有 checkout head,攻擊者輸入仍會出現在 PR 標題、分支名稱、提交訊息中,並被插值。
  • workflow_run — P0。與 pull_request_target 問題相同,但間接透過鏈結的 pull_request 工作流程的工件或元資料。
  • issue_commentissuespull_request_reviewpull_request_review_comment — P1。使用機密執行,任何能留言的人都能觸發。僅當工作流程未將使用者控制的欄位模板插值到 Shell 中時才安全。
  • push 搭配寬泛萬用字元branches: ['*'] 或無分支過濾)— P2。攻擊者若合併 PR,可透過推送後續分支來觸發特權工作流程。

針對每個發現:指出觸發條件名稱,說明為何在此特定工作流程環境中危險,並提出改寫建議(通常是 pull_request,有時拆分為兩個工作流程,有時「這需要 GitHub App 而非 Actions」)。

掃描 2:權限

檢查工作流程和工作層級的 permissions: 區塊。

  • 無頂層 permissions: 區塊 — P1。GITHUB_TOKEN 的預設權限取決於儲存庫和組織設定;在較舊的儲存庫上可能為 write-all。標記為:「在頂層加上 permissions: {},按工作授予權限。」
  • 任何地方的 permissions: write-all — P1。
  • 工作層級 permissions: 授予超出工作明確需求的權限 — P2。例如,僅執行測試的工作卻有 contents: write。建議最小權限原則。
  • 與掃描 1 的危險觸發條件結合 — 嚴重性提升一級。

掃描 3:Action 固定版本

檢查每個 uses: 行。

  • 固定到標籤uses: actions/checkout@v4@v4.1.1)— P1。標籤是可變的;攻擊者若入侵 Action 儲存庫,可強制推送標籤。
  • 固定到分支uses: actions/checkout@main)— P0。比標籤固定更糟;任何提交到該分支的內容都會立即流入。
  • 固定到 SHA 但無版本註解 — P3 風格發現。建議格式 uses: owner/action@<sha> # v4.1.1,以便固定版本更新的審查保持可讀。
  • 固定到看起來不尋常的 SHA(第三方 Action、可疑擁有者、近期建立的儲存庫)— 標記為需手動驗證;若無 GitHub API 無法確認冒名提交狀態,但值得注意。

標籤/分支固定的改寫方式一律為:替換為他們意圖版本的完整 40 字元提交 SHA,並加上 # vX.Y.Z 註解。

掃描 4:Shell 注入(模板注入)

針對每個 run: 區塊,掃描 ${{ ... }} 替換。

常數或非攻擊者控制的值沒問題(例如 ${{ matrix.os }}${{ secrets.MY_TOKEN }},但即使在某些環境中也有風險)。危險欄位包括:

  • github.event.pull_request.titlebodyhead.refhead.shahead.label
  • github.event.issue.titlebody
  • github.event.comment.bodyuser.login
  • github.event.review.body
  • github.head_ref
  • github.event.workflow_run.head_branchhead_commit.message
  • github.event.commits.*.messageauthor.nameauthor.email
  • 來自 workflow_dispatch 的任何 inputs.*(若工作流程在特權環境中執行)
  • 任何最終來自 actions/github-script、下載的工件或外部 API 回應的欄位

偵測規則:run: 區塊直接在腳本主體中包含 ${{ github.event.* }}${{ github.head_ref }},則為 P0 模板注入。修正方式一律為:

# 有漏洞
- run: echo "Branch is ${{ github.head_ref }}"

# 安全
- env:
    BRANCH: ${{ github.head_ref }}
  run: echo "Branch is $BRANCH"

同時標記(P1):

  • echo "VAR=${{ untrusted }}" >> $GITHUB_ENV — 環境檔案注入。攻擊者可透過包含換行符跳出變數。
  • echo "::set-env name=VAR::${{ untrusted }}" — 已棄用的工作流程指令,相同問題。
  • 將不受信任檔案 cat$GITHUB_ENV$GITHUB_OUTPUT 的內聯腳本。

掃描 5:不受信任的 Checkout

針對每個 actions/checkout 步驟:

  • ref: ${{ github.event.pull_request.head.sha }}(或 head.ref)位於由 pull_request_targetworkflow_run 觸發的工作流程中 — P0。這是典型的 pwn-request:特權環境執行 Fork 作者程式碼。
  • persist-credentials: true(預設值)用於不需要推回的工作流程 — P2。建議除非工作流程明確需要內嵌 Token,否則使用 persist-credentials: false

掃描 6:特權環境中的快取

針對每個使用 cache: 輸入的步驟(最常見於 actions/setup-nodesetup-pythonsetup-gosetup-java 或直接 actions/cache):

  • 在發行版或發布工作流程中快取 — P0。來自預設分支上任何其他工作流程的快取中毒,可將惡意建置輸入流入發行版。Trivy 和 TeamPCP 攻擊都透過此路徑。
  • 在處理機密的工作流程中快取 — P1。
  • 快取金鑰未限定範圍以防止不受信任的 PR 工作流程寫入與預設分支建置相同的金鑰 — P2。

發行版工作流程的改寫方式:完全移除 cache:,並加上註解說明原因(例如 # Do not cache: see https://github.com/actions/setup-node/issues/1445)。

掃描 7:工件注入

若工作流程從另一個工作流程下載工件(actions/download-artifactdawidd6/action-download-artifact 等):

  • 工件內容未經驗證就用於 run:$GITHUB_ENV — 若產生工作流程在不受信任程式碼上執行(例如來自 Fork 的 pull_request),則為 P0。攻擊者可將任意內容放入工件。
  • 建議嚴格驗證:若工件應為 PR 編號,則拒絕任何非數字內容。若為結構化檔案,則解析並驗證結構。

掃描 8:發行版特定強化

若工作流程看起來像發行版/發布工作流程(發布到 npm、PyPI、crates.io、Docker 註冊表;建立 GitHub 發行版;推送標籤):

  • 發布工作未宣告 environment: — P1。發行版憑證應限定於部署環境,而非儲存庫/組織機密。
  • 使用長期註冊表 Tokensecrets.NPM_TOKENsecrets.PYPI_TOKEN)而非 OIDC/Trusted Publishing — P2。建議相關註冊表的 OIDC 路徑。
  • 未產生證明actions/attest-build-provenance、npm 的 --provenance、PyPI 的 PEP 740)— P3 強化建議,非漏洞。
  • 發行版路徑中任何地方有快取 — P0,請見掃描 6。

掃描 9:自託管執行器

若工作流程使用 runs-on: 搭配非 GitHub 託管執行器(ubuntu-*windows-*macos-*):

  • 自託管執行器可被 Fork PR 觸及 — P0。自託管執行器在工作之間共享狀態,已導致重大入侵(PyTorch)。標記為需手動審查執行器範圍。
  • 此項超出預設威脅模型 — 記錄發現並建議使用者在 GitHub 設定中驗證執行器限制。

發現格式

以以下結構回報每個發現。按嚴重性分組,P0 優先。

[P0] template-injection in .github/workflows/ci.yml:23
  Run block interpolates github.event.pull_request.title directly into shell.
  An attacker controls the PR title and can execute arbitrary code in the
  workflow context, which has access to GITHUB_TOKEN.

  Vulnerable:
    - run: echo "Title: ${{ github.event.pull_request.title }}"

  Fix:
    - env:
        TITLE: ${{ github.event.pull_request.title }}
      run: echo "Title: $TITLE"

若使用者貼上原始 YAML 而無檔名,則稱之為「該工作流程」並使用片段內的行號。

嚴重性等級

  • P0 — 可立即利用,無需鏈結。Fork PR 作者或任意 GitHub 使用者可入侵機密、儲存庫內容或發行版。封鎖合併。
  • P1 — 需額外一步才能利用(例如需與另一個發現結合,或需維護者失誤)。若在發行版路徑上發現,則封鎖合併。
  • P2 — 強化缺口。無法直接利用,但若與未來錯誤結合,可減少爆炸半徑。在正常審查週期中修復。
  • P3 — 風格或一致性發現。值得為可讀性修復,無安全影響。

當工作流程看起來沒問題時

完成所有九個掃描後,若無任何觸發:

  1. 明確說明。「未發現違反標準規則集的問題。」
  2. 註明 檢查的項目:組織層級設定(預設 Token 權限、規則集強制執行、2FA)、儲存庫層級設定(分支保護、標籤保護、不可變發行版)、Action 原始碼(固定的 Action 本身是否在執行時安裝可變二進位檔),以及相依項目的執行時期行為。
  3. 建議使用者檢查 references/checklist.md 中「每個儲存庫」和「每個組織」下的項目 — 這些需要 GitHub 設定存取權限,而非工作流程 YAML。

乾淨的工作流程掃描不代表安全狀態良好。

參考檔案

  • references/triggers.md — 每個 GitHub Actions 觸發條件的詳細表格,說明每個觸發條件的危險或安全原因,以及人們常用危險觸發條件時的安全模式。當工作流程使用使用者特別詢問的觸發條件,或當您想解釋觸發條件為何危險(超越一行摘要)時,請閱讀此檔案。
  • references/checklist.md — 每個工作流程/每個儲存庫/每個組織的平面檢查清單。當使用者要求完整稽核、掃描多個儲存庫或大規模分類時很有用。包含分類優先順序。
  • references/patterns.md — 常見的「我想安全地做 X」模式。當使用者詢問如何 取代 被標記的危險模式(而非僅識別)時,請閱讀此檔案。

此技能不會做的事

  • 不會安裝 zizmor、pinact 或其他工具。掃描是模型讀取 YAML 並套用這些規則。
  • 除非使用者明確問「我應該執行什麼工具」,否則不會建議安裝工具。即使如此,直接指向模式 — 此技能中的規則 就是 那些工具編碼的稽核。
  • 不會用緩解措施來粉飾 pull_request_target 的發現。若工作流程使用該觸發條件且未使用 GitHub App,則為開放性發現。
  • 不會告訴使用者一切沒問題而不說明檢查了什麼和未檢查什麼。對「這樣安全嗎」的預設回答是「以下是掃描涵蓋的範圍以及發現的結果。」