VulnCheck CLI を使えば、あらゆる CI プラットフォームを脆弱性ゲートに変えられます。リポジトリに対して vulncheck scan を実行すると、SBOM を生成し、すべてのコンポーネントを VulnCheck インテリジェンスと照合して、CVSS、Temporal スコア、EPSS、SSVC、VulnCheck KEV への該当情報を付加した CVE を返します。
GitHub では VulnCheck Action がこれらをすべて内包しています。それ以外の環境でも、シェル 5 ステップで同じ結果が得られます。インストール、認証、スキャン、ゲート、公開です。本ページがそのパターンであり、各プラットフォームのガイドはこれを適用したものです。
| プラットフォーム | ガイド | 統合方法 |
|---|---|---|
| GitHub Actions | VulnCheck in GitHub Actions | ターンキー vulncheck-oss/action |
| GitLab CI/CD | VulnCheck in GitLab CI/CD | CLI |
| Jenkins | VulnCheck in Jenkins | CLI |
| Azure Pipelines | VulnCheck in Azure Pipelines | CLI |
| Bitbucket、CircleCI、Drone、Woodpecker、Buildkite | その他の CI プラットフォーム | CLI |
api.vulncheck.com および github.com(CLI のダウンロード用)への HTTPS 通信。curl、tar、jq。スキャン結果を合否の判断に変換するのが jq です。バージョンを固定し、リリース tarball からインストールします。CI ではこの方法を推奨します。結果が再現可能で、GitHub API を呼び出さず、コンテナ内の root でも動作します。
VC_CLI_VERSION=1.1.0
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
install -m 0755 "/tmp/vulncheck_${VC_CLI_VERSION}_linux_${ARCH}/bin/vulncheck" /usr/local/bin/vulncheck
vulncheck version
install.sh は開発端末向けのスクリプトであり、CI では次の 2 点が問題になります。Unsupported operating system として終了します。--sudo は sudo が存在することを前提にしています。root で実行しているコンテナでは sudo: command not found と表示した後に Installation complete! と報告し、何もインストールしないまま終了コード 0 を返します。問題が表面化するのは、後続の vulncheck: command not found の時点です。バイナリ自体は静的リンクされており、Alpine を含めどこでも動作します。glibc が必要なのはインストールスクリプトだけです。musl 系イメージでは上記の tarball を使用してください。
トークンは CI プラットフォームのマスクされたシークレットとして保存し、VC_TOKEN として公開します。CLI はこれを自動的に読み取るため、CI で auth login を実行する必要はありません。
vulncheck auth status --json | jq -e '.authenticated'
VC_TOKEN は保存済みの設定ファイルより優先されます。また CLI は CI を自動検出し、CI、BUILD_NUMBER、RUN_ID のいずれかが設定されていれば --no-interactive として扱うため、プロンプトで停止することはありません。
vulncheck scan . --json > scan.json
jq -r '"\((.vulnerabilities // []) | length) vulnerabilities found"' scan.json
結果をファイルに書き出し、サマリーを自分で出力することで、ビルドログが読みやすくなります。--json モードでは、検出結果がない場合の出力は {"schema_version": 1} のみで、メッセージは一切表示されません。
結果は検出結果の配列で、ポリシー判定に必要な情報がすべて含まれます。
{
"schema_version": 1,
"vulnerabilities": [
{
"name": "requests",
"version": "2.19.1",
"cve": "CVE-2018-18074",
"in_kev": false,
"cvss_base_score": "7.5",
"cvss_temporal_score": "7.1",
"fixed_versions": "2.20.0",
"metrics": { "epss": { "epss_score": 0.00182 }, "ssvc": [] }
}
]
}
vulncheck scan は脆弱性を検出したかどうかに関係なく終了コード 0 を返します。重大な CVE の検出はスキャンの成功であり、コマンドの失敗ではありません。したがって、どのパイプラインにも明示的なゲートが必要です。ゲートがなければ、ログに検出結果が残ったままジョブは成功します。次のゲートは、CVSS 基本値がしきい値以上の検出結果、またはスコアを問わず VulnCheck KEV に該当する検出結果があればビルドを失敗させます。
VC_CVSS_THRESHOLD=7.0
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 "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
}
// [] は必須です。検出結果がない場合、スキャン結果には vulnerabilities キー自体が含まれません。ガードなしで反復処理すると jq は Cannot iterate over null として終了コード 5 を返し、何も問題のないリポジトリでビルドが失敗します。
cvss_base_score のガードも同様です。すべての検出結果に CVSS スコアが付いているとは限らず、値が欠落・null・空文字列の場合、tonumber は .in_kev が評価される前に jq プログラム全体を同じ終了コード 5 で中断させます。.cvss_base_score // 0 | tonumber? // 0 と書けば、そうした検出結果はスコア 0 として扱われるため、スコアのない VulnCheck KEV 該当項目もゲートを壊すことなく in_kev 側の条件で捕捉できます。
しきい値を高く設定している場合でも、in_kev によるゲートは残す価値があります。既知の悪用が確認されている脆弱性は、スコアに関係なく重要です。ゲートに使用できるフィールドとしては、他に cvss_temporal_score、metrics.epss.epss_score、metrics.ssvc[].exploitation があります。
scan.json はビルドアーティファクトとして保存してください。ビルド時点で判明していた内容の記録になります。CLI は CycloneDX 1.7 形式の SBOM も出力できるため、あわせて公開することをおすすめします。
vulncheck scan . --sbom-only -o sbom.json
スキャン結果によって終了コードが変わることはありませんが、失敗時には変わります。パイプラインが壊れた場合は次を確認してください。
| コード | 意味 | CI でよくある原因 |
|---|---|---|
| 0 | 成功 | 脆弱性が検出された場合も含む |
| 1 | 内部エラー | scan のパスを解決できない(下記参照) |
| 2 | 入力検証エラー | 引数やフラグの誤り |
| 3 | 認証エラー | VC_TOKEN が未設定、期限切れ、またはジョブに渡っていない |
| 4 | 対象が見つからない | 該当するインデックスやリソースが存在しない |
| 5 | レート制限 | 複数のジョブが 1 つのトークンを共有している |
| 6 | ネットワークエラー | ランナーから api.vulncheck.com に到達できない |
--json モードでは、エラーは構造化されたエンベロープとして標準出力に返されるため、そのまま判定に使えます。
{
"schema_version": 1,
"error": {
"code": "auth_invalid",
"message": "unauthorized: token is missing or invalid",
"http_status": 401
}
}
code: "internal" が返り、メッセージには snap、docker、podman、containerd、OCI レジストリの解決失敗が列挙されます。scan はコンテナイメージの参照も受け付けるため、指定された値をイメージとして解決しようとするからです。プロバイダーの解決エラーが表示された場合は、まず指定したパスを確認してください。インターネットに接続できないランナーでは、ローカルにキャッシュしたインデックスに対して vulncheck scan --offline でスキャンできます。インデックスの同期とキャッシュについては オフラインモード、その元データについては オフラインバックアップ を参照してください。