
RMM管理下のフリート向けのオープンソース、単一スクリプトのCVEスキャナー。純粋なPowerShell 7 — RMMソフトウェアインベントリをNVD、CISA KEV、EPSS、SSVCと結合し、このバージョンは脆弱か、そしてその緊急性はどの程度かを答えます。
# fleet-cve-scanner RMM管理下のフリート向けのオープンソース、単一スクリプトのCVEスキャナー。純粋なPowerShell 7で、エージェント不要、アプライアンス不要、ライセンス料無料。 管理下のすべてのエンドポイント上のすべてのソフトウェアに対して、1つの質問に答えます:**インストールされているバージョンが脆弱であることが知られているか、そしてその緊急度はどの程度か?** — RMMのソフトウェアインベントリを、無料で信頼できるセキュリティフィードと結合することで実現します: - **NIST NVD** — 既知のCVE + 影響を受けるバージョン範囲 - **CISA KEV** — 実際に悪用されているもの(スコアしきい値をバイパス) - **EPSS** — 今後30日以内に悪用される確率 - **CISA SSVC** — 行動/注意/追跡の優先順位付け判断 - **MSRC** — Microsoftのパッチがすでにその発見をカバーしているという証拠 - **endoflife.date** — 二度とパッチが提供されないソフトウェア 出力:デバイスごとのCSVレポート、SLAクロックと週次変更イベントを含むSQLite履歴、自己完結型のHTMLダッシュボード、オプションのMarkdown公開レポート。 ## ステータス **v1.0.0.** エンジンは、Windowsフリートに対して本番環境で週次実行されていたスキャナーからクリーンルームで移植されました([docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md)参照)。このリリースの前に、2つを同一のライブフリートに対して実行し、行ごとに比較しました:両方が同一の*(デバイス、ソフトウェア、バージョン)*を確認した数万の検出結果にわたって、すべての計算フィールド — ステータス、CVSS、深刻度、KEVフラグ、SSVC判断、修正バージョン — が完全に一致しました。 この比較は**判定**ロジックをカバーしています。履歴/SQLレイヤーはテストされておらず、お客様の環境でツールを検証する代わりにはなりません。信頼する前に[docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md)をお読みください — これはお世辞ではなく正直に書かれています。 ## クイックスタート [PowerShell 7](#要件)(`pwsh`)が必要です。2つの実行方法: ### A. ライブ NinjaOne スキャン ```bash cp config.example.json config.json # config.json を編集: ninjaone.client_id / client_secret / base_url を記入、 # output.report_dir を設定し、(推奨)nvd.api_key を設定 pwsh -File fleet-cve-scan.ps1 ``` ### B. オフライン — 任意のRMM、SCCM、またはIntune(APIアクセス不要) インベントリをCSVにエクスポートします(列:`hostname, software, version`、オプションで`device_id, os`)。その後: ```bash cp config.example.json config.json # ここでは output.report_dir のみ必要 pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv ``` `-InputCsv` は**ゼロ**のNinjaOne呼び出しを行います — OAuthもAPIも不要です。それでもNVDやその他の公開フィードをクエリするため、`config.json` に `nvd.api_key` と有効な `output.report_dir` が必要です。入力契約とRMMごとのエクスポートレシピについては[docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md)を参照してください。 ### 前提条件のインストール - **PowerShell 7** — macOS: `brew install powershell`; Windows: `winget install Microsoft.PowerShell`; Debian/Ubuntu: Microsoftパッケージリポジトリ(`packages.microsoft.com`)からインストール。 - **sqlite3**(オプション、履歴/トレンドを有効にする) — macOS: プリインストール済みまたは `brew install sqlite`; Debian/Ubuntu: `apt-get install sqlite3`; Windows: SQLiteの「tools」バンドルをsqlite.orgからダウンロードし、`sqlite3.exe` を `PATH` に置くか、スクリプトと同じディレクトリに置く。これがない場合でもスキャンは実行されますが、`first_seen` は実行日にフォールバックします。 ## 要件 - **PowerShell 7**(7.4+ 推奨)。並列スキャンには7.0+が必要です。 - **sqlite3** — オプション。履歴DB、トレンド、SLA `first_seen` 追跡を有効にします。ない場合 = それらの機能はスキップされ、スキャンは完了します。 - **ネットワーク出力** — NVD、CISA(KEV)、`epss.empiricalsecurity.com`(EPSSバルクスコアホスト — FIRSTがEPSSデータホスティングをそこに移行)、MSRC、endoflife.date、GitHub raw(CVE Program `cvelistV5`) — さらにライブモードではNinjaOne API。 - **認証情報** — ライブモードではNinjaOne OAuth2クライアントID/シークレットが必要。オフラインモードではインベントリCSVのみが必要。NVD APIキーは技術的にはオプションですが、**フリート規模では事実上必須**:NVDではキーありで50リクエスト/30秒、キーなしで5/30秒が許可されています。スキャナーはどちらを持っているかを検出し、それに合わせてスロットリングするため、キーなしの実行は正しいですが、約10倍遅くなります — 少数の製品には問題ありませんが、実際のフリートには実用的ではありません。[キーは無料です。](https://nvd.nist.gov/developers/request-an-api-key) ## 設定リファレンス 設定はJSONファイルです(デフォルトはスクリプトと同じディレクトリの `config.json`。`-ConfigPath` で上書き可能)。[`config.example.json`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/config.example.json) から始めてください。スキャナーが読み取るすべてのキー、省略時のデフォルト値、その機能: ### `ninjaone`(ライブモード) | キー | デフォルト | 目的 | | --- | --- | --- | | `ninjaone.client_id` | `""` | NinjaOne API OAuth2クライアントID。ライブスキャンに必須。`-InputCsv` の場合は空白のままにします。 | | `ninjaone.client_secret` | `""` | NinjaOne API OAuth2クライアントシークレット。 | | `ninjaone.base_url` | —(例では `https://app.ninjarmm.com`) | NinjaOne APIベースURL。ライブスキャンに必須。組み込みのフォールバックはありません。リージョンのホストを使用してください(例:`eu.ninjarmm.com`、`oc.ninjarmm.com`)。 | ### `nvd` | キー | デフォルト | 目的 | | --- | --- | --- | | `nvd.api_key` | `""` | NVD APIキー。`apiKey` ヘッダーとして送信されます。空白の場合は未認証で実行され、`nvd_rate_limit` と `nvd_min_spacing_ms` のデフォルトがNVDの匿名上限(4/30秒、6500ms)に低下します(認証済みの場合は48/30秒、700ms)。無料で、約10倍高速です。 | ### スキャンとスコアリング | キー | デフォルト | 目的 | | --- | --- | --- | | `cvss_threshold` | `7.0` | スコア付きCVEがVULNERABLEと報告されるための最小CVSSベーススコア。KEVリストに記載されたCVEはこの下限をバイパスします。 | | `ssvc_mission_prevalence` | `high` | SSVC Mission Prevalenceのステークホルダー入力(`low` / `medium` / `high`)。 | | `ssvc_public_wellbeing` | `high` | SSVC Public Well-beingのステークホルダー入力(`low` / `medium` / `high`)。 | ### キャッシュとレート制限 | キー | デフォルト | 目的 | | --- | --- | --- | | `nvd_cache_ttl_days` | `7` | NVD結果(ソフトウェア名ごと)が再クエリされるまでキャッシュされる日数。 | | `nvd_cache_flush_every` | `500` | NVDキャッシュをディスクにチェックポイントする完了アイテム数。これにより、強制終了されたスキャンでもフェッチが保持されます。`0` で無効化(スキャン終了時の保存は引き続き実行されます)。 | | `nvd_min_spacing_ms` | APIキーあり: `700`、なし: `6500` | NVD呼び出し間の最小ミリ秒数。**バインディングレート制約** — 最初にこれを調整してください。 | | `nvd_rate_limit` | APIキーあり: `48`、なし: `4` | 30秒あたりのNVD呼び出しのスライディングウィンドウ上限(バックストップ)。下限は1。 | | `parallel_throttle` | `20` | `ForEach-Object -Parallel` の実行スペース数。NVD呼び出しは関係なく直列化されます。これはキャッシュヒットの並行性のみを制限します。下限は1。 | | `kev_cache_ttl_hours` | `24` | キャッシュされたCISA KEVカタログのTTL。 | | `epss_cache_ttl_hours` | `24` | キャッシュされたFIRST.org EPSSスコアファイルのTTL。 | | `eol_cache_ttl_days` | `7` | キャッシュされたendoflife.dateルックアップのTTL。 | | `msrc_cache_ttl_days` | `30` | キャッシュされたMSRC CVRFデータのTTL。 | | `cvelist_cache_ttl_days` | `7` | キャッシュされたCVE Program(`cvelistV5`)/ CISA-ADP SSVCデータのTTL。 | | `cvelist_fetch_budget` | `300` | SSVC/vulnrichmentエンリッチメントのための、1回の実行あたりの最大CVEレコードフェッチ数。残りは次回実行に延期されます。 | `nvd_min_spacing_ms`、`nvd_rate_limit`、および `parallel_throttle` はスキャナーによって読み取られますが、`config.example.json` には含まれていません — チューニングが必要な場合のみ追加してください。[docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md) を参照してください。 ### エンリッチメントとSLA | キー | デフォルト | 目的 | | --- | --- | --- | | `epss_spike_threshold` | `0.10` | スキャン間での発見のEPSSスコアの最小上昇値。`EPSS_SPIKE` 変更イベントを発行します。 | | `sla_days_critical` | `14` | CRITICAL発見のSLAウィンドウ(`first_seen` からの日数)。 | | `sla_days_high` | `30` | その他のスコア付き深刻度のSLAウィンドウ。 | | `denied_software_csv` | `""` | オプションの拒否ソフトウェアCSVクロスチェックへのパス。空の場合はチェックを無効にします。 | ### `output` | キー | デフォルト | 目的 | | --- | --- | --- | | `output.report_dir` | *(必須)* | すべての出力 — CSV、履歴DB、ダッシュボード、キャッシュ — のディレクトリ。 | | `output.wiki_security_page` | `""` | オプションのMarkdown公開ページへのパス。設定されている場合のみ書き込まれます。 | | `output.dashboard_share` | `""` | 各実行後にダッシュボードのコピーを受け取るオプションのパス/共有。空の場合はローカルのみ。 | ## 出力 以下はすべて `output.report_dir` に出力されます。 - **`vuln-report-YYYY-MM-DD.csv`** — 発見ごとのレポート。28列(スキーマは以下)。 - **`vuln-history.db`** — SQLite履歴(`sqlite3` が必要):`runs`(実行ごとの集計)、`findings`(`(device, software, cve)` ごとのオープン発見履歴、`first_seen` / `last_seen` / `resolved_date` / `epss_score` を含む)、および `changes`(型付けされた週次イベント)。 - **`cve-dashboard.html`** — 自己完結型HTMLダッシュボード(チャートライブラリはインライン化、外部リクエストなし)。オプションで `output.dashboard_share` にコピーされます。 - **`eol-report-YYYY-MM-DD.csv`** — サポート終了ファミリーが検出された場合に書き込まれます。 - **`denied-report-YYYY-MM-DD.csv`** — `denied_software_csv` が設定され、一致した場合に書き込まれます。 - **キャッシュファイル** — `nvd-cache.json`、`kev-cache.json`、`epss-cache.json`、`eol-cache.json`、`msrc-cache.json`、`cvelist-cache.json`(すべてgitignore対象)。 - **Markdown公開ページ** — `output.wiki_security_page` に、設定されている場合のみ。 ### CSVスキーマ(28列) 列セットは固定されており、ゲートで強制されます。順に: | # | 列 | 意味 | | --- | --- | --- | | 1 | `device_id` | RMMデバイスID(または `-InputCsv` モードでは決定論的な合成ID)。 | | 2 | `hostname` | デバイスのホスト名。 | | 3 | `os` | オペレーティングシステム文字列(空白の場合あり)。 | | 4 | `software` | インベントリされた製品名。 | | 5 | `installed_version` | インベントリされたバージョン文字列。 | | 6 | `cve_id` | CVE識別子。 | | 7 | `cvss_score` | CVSSベーススコア(なしの場合は `0`)。 | | 8 | `severity` | CVSS深刻度ラベル。 | | 9 | `cvss_version` | スコアを生成したCVSS標準(`4.0`/`3.1`/`3.0`/`2.0`/空白)。 | | 10 | `epss_score` | FIRST.org EPSS確率(CVEがカタログにない場合は空白)。 | | 11 | `epss_percentile` | EPSSパーセンタイル(ない場合は空白)。 | | 12 | `ssvc_exploitation` | 生のCISA SSVC悪用オプション(`none`/`poc`/`active`;ADPデータがない場合は空白)。 | | 13 | `ssvc_decision` | SSVC判断 `Act`/`Attend`/`Track`(ADPデータがない場合は空白)。 | | 14 | `status` | `VULNERABLE` / `PATCHED` / `UNKNOWN`。 | | 15 | `published_date` | CVE公開日。 | | 16 | `unknown_reason` | 行がUNKNOWNである理由(`SUSPECT_VERSION` / `NO_VERSION_BOUND` / `VERSION_PARSE_ERROR`;それ以外は空白)。 | | 17 | `is_kev` | CVEがCISA KEVカタログにある場合 `True`。 | | 18 | `kev_due_date` | CISA KEV修復期限日。 | | 19 | `kev_ransomware_use` | KEVランサムウェアキャンペーン使用フラグ。 | | 20 | `fix_version` | 計算された修正境界バージョン。 | | 21 | `fix_boundary_inclusive` | 修正境界が包括的かどうか。 | | 22 | `advisory_url` | ベンダーアドバイザリまたはNVD詳細URL。 | | 23 | `patch_evidence` | Microsoft UNKNOWNがPATCHEDに変更された場合の証拠。 | | 24 | `triage_status` | `triage.json` からのトリアージ処分(なしの場合は空白)。 | | 25 | `triage_note` | トリアージノート。 | | 26 | `first_seen` | 発見が最初に観測された日付(履歴DBから)。 | | 27 | `sla_due` | SLA期限日。 | | 28 | `sla_breach` | SLAが違反されているかどうか。 | ## スケジューリング `Run-WeeklyScan.ps1` はスケジュール実行用のラッパーです:自身のディレクトリからスキャナーを実行し、**すべての**出力ストリームを `<スクリプトディレクトリ>/logs/` の下の日付付きログにキャプチャし(`-LogDir` で上書き可能)、スキャナーの終了コードを伝搬してスケジューラーが失敗を認識できるようにします。 **Windows タスクスケジューラ:** ```bat schtasks /Create /TN "CVE-Scan-Weekly" ^ /TR "pwsh -NoProfile -File C:\path\to\Run-WeeklyScan.ps1" ^ /SC WEEKLY /D SUN /ST 22:00 /RU SYSTEM ``` **cron(Linux/macOS):** ```cron 0 22 * * 0 pwsh -File /path/to/Run-WeeklyScan.ps1 ``` ## ダッシュボードホスティング `cve-dashboard.html` は完全に自己完結型です — 直接開くか、任意の静的ホストまたはファイル共有に配置するか、`output.dashboard_share` を使用して各実行後に自動的にコピーできます。アクセス制御されたホスティングには、[`dashboard-auth/`](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth) ディレクトリにMicrosoft Entra(Azure AD)認証付きの最小限のASP.NETホストが同梱されています。その[README](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/README.md)と[RUNBOOK](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/dashboard-auth/RUNBOOK.md)を参照してください。 ## 正直な制限事項(信頼する前に読んでください) - **CPEマッチングは本質的にあいまいです。** 典型的なインベントリの40〜60%にはNVDエントリがまったくなく、スキップされます(ログに記録され、決して黙って無視されることはありません)。名前ベースのCPE選択は間違った製品を選ぶ可能性があります。抑制ワークフローが存在するのは、その必要があるからです。これは無料NVDアプローチの上限です — 厳選された検出カタログを持つ有料スキャナーの方が正確です。このツールの価値は、無料で透明性があり、監査可能であることです。 - **UNKNOWNは第一級の判定です。** スキャナーが脆弱またはパッチ済みを証明できない場合、推測する代わりにそのように報告します。 - **NinjaOneに対してのみ検証済み。** スキャンエンジンはRMMに依存しません(入力は単にデバイス+ソフトウェア+バージョン)。`-InputCsv` モードにより、任意のRMM/SCCM/Intuneエクスポートをフィードできますが、本番環境で実行されたのはNinjaOneアダプターのみです。 完全で率直なリスト — CPEスキップ率、重複キーのマスキング、page-1の `totalResults`、MSRCの優先順位の隙間など — は[docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md)にあります。 ## 設計ノート 一見型破りに見えるいくつかの構造上の選択は意図的です: - **モジュールではなく単一ファイル。** スキャンのホットパスは `ForEach-Object -Parallel` 内で実行され、それらの実行スペースは外側のスコープの関数を見ることができません — そのため、いくつかのヘルパーが並列ブロック内で意図的に重複しています。1ファイルに1関数のモジュールレイアウトでは、その重複は除去されません(並列ボディは依然として実行スペースごとにソースを注入する必要があります)。ビルドステップが追加され、1ファイルコピーのデプロイモデルが壊れるだけです。プロジェクトがいつか単一ファイルを超える場合、パッケージ化はv2の形の変更であり、パッチではありません。 - **`[PSCustomObject]` レコード、クラスではない。** 発見レコードは並列実行スペース内で生成され、メインの実行スペースにマーシャリングされます。PowerShellクラスインスタンスはその境界を確実に超えられません。`[PSCustomObject]` はそれを超えられ、CSV列契約は型システムではなくリリースゲートによって強制されます。 - **PesterではなくプレーンなPS7テストスクリプト。** `tests/` 内のスイートは、終了コードアサーションを持つ依存関係のないスクリプトであり、`pwsh` が存在する任意の場所(スキャンサーバーを含む)で、何もインストールせずに実行可能です。CIはWindowsとUbuntuの両方ですべてを実行します。 ## ドキュメント - [docs/how-it-works.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/how-it-works.md) — スキャンパイプラインを段階ごとに、判定ロジック、キャッシュ規律、フェイルソフト契約。 - [docs/known-limitations.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/known-limitations.md) — スキャナーが誤る可能性のあるすべての既知の方法とその理由。 - [docs/rate-limiting.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rate-limiting.md) — NVDレート制限の教訓をオペレーターガイドおよびチューニングテーブルとして。 - [docs/rmm-adapters.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/rmm-adapters.md) — 入力契約、2つの組み込みインベントリパス、およびネイティブアダプターの追加方法。 - [docs/HISTORY.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/docs/HISTORY.md) — 圧縮されサニタイズされたバージョン履歴と、各リリースの背後にあるエンジニアリングの教訓。 ## 貢献 PR歓迎 — 特にRMMアダプター。基本ルールと安全な方向性の要件(ステータスロジックは「より多くの」可視性に向かってのみ誤る可能性があり、決して黙ってPATCHEDに向かってはならない)については[CONTRIBUTING.md](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/CONTRIBUTING.md)を参照してください。 ## データソースと帰属 このツールは、結合する無料で信頼できるフィードと同じくらい有用です。それぞれがスキャン時にクエリされます。これに基づいて構築する場合は、それぞれの利用規約を尊重してください。 - **NIST National Vulnerability Database (NVD)** — CVEレコードと影響を受けるバージョン範囲。NVD API経由。この製品はNVD APIを使用していますが、NVDによって承認または認定されていません。<https://nvd.nist.gov/> - **CISA Known Exploited Vulnerabilities (KEV) Catalog** — 実際に悪用されていることが知られているCVEの信頼できるリスト。<https://www.cisa.gov/known-exploited-vulnerabilities-catalog> - **CVE Program — `cvelistV5`** — CVEレコードと、CISA ADPによって提供されるSSVC/vulnrichmentデータ。CVE®はMITRE Corporationの登録商標です。<https://github.com/CVEProject/cvelistV5> - **SSVC (Stakeholder-Specific Vulnerability Categorization)** — このツールの行動/注意/追跡判断ロジックは、CERT/CC SSVC(カーネギーメロン大学ソフトウェア工学研究所)およびCISA SSVCコーディネーター決定木バージョン2.0.3に基づいています。CERT/CCおよびCISAへのクレジットが必要です。<https://www.cisa.gov/ssvc> · <https://github.com/CERTCC/SSVC> - **FIRST.org EPSS (Exploit Prediction Scoring System)** — 今後30日以内の悪用確率。EPSSデータはFIRSTによって提供されています。Jay Jacobs、Sasha Romanosky、Benjamin Edwards、Michael Roytman、Idris Adjerid(2021)、*Exploit Prediction Scoring System*、Digital Threats: Research and Practice、2(3)を参照。<https://www.first.org/epss/> - **Microsoft Security Response Center (MSRC)** — CVRF API。Microsoftセキュリティ更新プログラムがすでに発見をカバーしていることを確認するために使用されます。© Microsoft Corporation。<https://api.msrc.microsoft.com/> · <https://msrc.microsoft.com/> - **endoflife.date** — 二度とパッチが提供されないソフトウェアのサポート終了日とサポート終了日。データはendoflife.dateプロジェクトのご厚意によるものです。<https://endoflife.date/> ## ライセンス Apache-2.0 — [LICENSE](https://github.com/boostedchaos/fleet-cve-scanner/blob/main/LICENSE)を参照。