CI/CD パイプライン

GitHub Actions

VulnCheck Action でプルリクエストをスキャンし、検出結果をレビューに直接投稿します。

VulnCheck Action は、VulnCheck が提供するターンキーの CI 統合です。VulnCheck CLI を内包し、プルリクエストごとにリポジトリをスキャンし、検出結果があれば実行を失敗させたうえで、その結果をプルリクエストのコメントとして投稿します。レビュー担当者はレビュー画面を離れることなく内容を確認できます。

クイックスタート

VulnCheck API トークンをリポジトリまたは Organization のシークレットとして保存します(以下の例では VC_TOKEN)。その上でワークフローを追加します。

name: Scan with VulnCheck

on:
  pull_request:
    branches:
      - main

permissions: write-all

jobs:
  scan:
    name: Scan with VulnCheck
    runs-on: ubuntu-latest
    steps:
      - uses: vulncheck-oss/action@v1
        with:
          command: scan
          token: ${{ secrets.VC_TOKEN }}

これで統合は完了です。この Action 自体はチェックアウトを行わないため、ワークフローの他のステップで作業ツリーが必要な場合は先に actions/checkout を追加してください。

token は VulnCheck API トークンです。API トークン から作成し、シークレットとして保存してください。ワークフローファイルに直接記述しないでください。

入力

必須の入力は token のみです。

入力説明既定値
tokenVulnCheck API トークン
command実行するコマンドscan
scan-pathスキャン対象のパス./
scan-cvss-base-thresholdCVSS 基本値のしきい値
scan-cvss-temporal-thresholdCVSS Temporal 値のしきい値
scan-cve-details検出結果ごとにパッケージ種別、カタロガー、検出箇所をアノテーションとして出力するfalse
scan-cve-npm-relnpm を使用して CVE のパッケージから所有者を辿るfalse
disable-pr-commentスキャン結果をプルリクエストのコメントとして投稿しないfalse
github-token認証済み GitHub クライアントの作成に使用するトークン${{ github.token }}

scan-cve-details を有効にすると、各検出結果はワークフローのアノテーションとしても出力されます。

Notice: CVE-2021-23337 found in npm package lodash in /package-lock.json using javascript-lock-cataloger
Notice: CVE-2021-44906 found in npm package minimist in /package-lock.json using javascript-lock-cataloger

ビルドを失敗させる条件

CLI とは異なり、Action は成否の判定まで行います。

設定Action が実行を失敗させる条件
しきい値を設定しない深刻度を問わず、脆弱性が 1 件でも見つかった場合
scan-cvss-base-threshold または scan-cvss-temporal-threshold を設定いずれかのしきい値以上の検出結果があった場合

既定の動作は厳格です。深刻度の低い検出結果が 1 件あるだけで実行が失敗します。実際にポリシーに違反するものだけに絞り込むには、しきい値を設定してください。

- uses: vulncheck-oss/action@v1
  with:
    command: scan
    token: ${{ secrets.VC_TOKEN }}
    scan-cvss-base-threshold: '7.0'

また、いずれかのしきい値を設定すると、プルリクエストのコメントの形式も変わります。検出結果が 1 つの一覧ではなく、しきい値以上のものとしきい値未満のものに分かれて表示されます。

本セクションの他のプラットフォームと異なるのは、この点だけです。vulncheck scan 単体は常に終了コード 0 を返すため、CLI を使うレシピでは明示的な jq ゲートを追加しています。Action は自身の JavaScript でポリシー判定を行うので、その上にさらにゲートを追加しないでください。

出力

出力説明
scan-outputスキャンの結果
scan-count検出された脆弱性の件数
scan-signature結果の SHA256 ハッシュ。実行間の変化の検出に使用

これらは結果を取得するためのものであり、ゲートに使うものではありません。後続のステップが値を読み取れる時点で Action はすでに実行を失敗させているため、出力を使うステップには if: always() が必要です。

- uses: vulncheck-oss/action@v1
  id: vulncheck
  with:
    command: scan
    token: ${{ secrets.VC_TOKEN }}
    scan-cvss-base-threshold: '7.0'

- name: Save the scan results
  if: always()
  env:
    SCAN_COUNT: ${{ steps.vulncheck.outputs.scan-count }}
    SCAN_OUTPUT: ${{ steps.vulncheck.outputs.scan-output }}
  run: |
    echo "$SCAN_COUNT vulnerabilities found"
    printf '%s' "$SCAN_OUTPUT" > scan.json

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: vulncheck-scan
    path: scan.json

出力を run: スクリプトに直接展開せず env: 経由で読み取ることで、スキャン結果に由来する値がシェルとして解釈されるのを防げます。

権限

プルリクエストへのコメント投稿には書き込み権限が必要です。

permissions: write-all

権限を付与したくない場合は disable-pr-comment: true を設定してください。コメントは結果の表示手段であってゲートそのものではないため、検出結果があれば実行は変わらず失敗します。失われるのはレビュー画面上のサマリーだけです。

フォークからのプルリクエストはスキャンできません。GitHub はフォークから作成された pull_request の実行にリポジトリのシークレットを渡さないため、secrets.VC_TOKEN は空のまま渡され、Action は認証エラーで失敗します。ジョブに if: github.event.pull_request.head.repo.full_name == github.repository を指定してこれらの実行をスキップし、マージ後のコードはデフォルトブランチでのスキャンでカバーしてください。

同じプルリクエストで再実行した場合、Action は新しい scan-signature を以前のコメントと比較します。結果が変わっていなければ再度コメントせず、変わっていればその変化を投稿します。

バージョンの固定

@v1 は v1 系の最新リリースを追跡します。厳密に固定する場合は vulncheck-oss/action@v1.1.5 のようなリリースタグ、あるいは組織のポリシーで不変な参照が求められる場合はコミット SHA を指定してください。

セルフホストランナーと GitHub Enterprise Server

この Action は node24 上で動作し、ランナー側に追加のツールを必要としません。ポリシーによりサードパーティ製 Action が禁止されている場合や、パブリックな Marketplace にアクセスできない GitHub Enterprise Server では、CLI を直接実行してください。結果は同じで、Marketplace への依存もありません。

name: Scan with VulnCheck CLI

on:
  pull_request:

jobs:
  scan:
    runs-on: ubuntu-latest
    env:
      VC_TOKEN: ${{ secrets.VC_TOKEN }}
      VC_CLI_VERSION: 1.1.0
      VC_CVSS_THRESHOLD: '7.0'
    steps:
      - uses: actions/checkout@v6

      - name: Install the VulnCheck CLI
        run: |
          ARCH="$(uname -m)"
          case "$ARCH" in x86_64) ARCH=amd64 ;; aarch64) ARCH=arm64 ;; esac
          curl -sSL "https://github.com/vulncheck-oss/cli/releases/download/v${VC_CLI_VERSION}/vulncheck_${VC_CLI_VERSION}_linux_${ARCH}.tar.gz" | tar -xz -C /tmp
          sudo install -m 0755 "/tmp/vulncheck_${VC_CLI_VERSION}_linux_${ARCH}/bin/vulncheck" /usr/local/bin/vulncheck
          vulncheck version

      - name: Scan
        run: |
          vulncheck scan . --json > scan.json
          jq -r '"\((.vulnerabilities // []) | length) vulnerabilities found"' scan.json

      - name: Gate on findings
        run: |
          jq -e --argjson max "$VC_CVSS_THRESHOLD" '
            [ (.vulnerabilities // [])[]
              | select((.cvss_base_score // 0 | tonumber? // 0) >= $max or .in_kev) ] | length == 0
          ' scan.json > /dev/null || {
            echo "::error::Findings at or above CVSS ${VC_CVSS_THRESHOLD}, or in VulnCheck KEV"
            jq -r --argjson max "$VC_CVSS_THRESHOLD" '
              (.vulnerabilities // [])[]
              | select((.cvss_base_score // 0 | tonumber? // 0) >= $max or .in_kev)
              | "  \(.cve)  \(.name)@\(.version)  CVSS \(.cvss_base_score)  KEV \(.in_kev)  fixed in \(.fixed_versions // "n/a")"
            ' scan.json
            exit 1
          }

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: vulncheck-scan
          path: scan.json

各ステップの意味と、ゲートを別ステップにしている理由については CI/CD での VulnCheck を参照してください。