
RMM管理下のフリート向けのオープンソース、単一スクリプトのCVEスキャナー。純粋なPowerShell 7 — RMMソフトウェアインベントリをNVD、CISA KEV、EPSS、SSVCと結合し、このバージョンは脆弱か、そしてその緊急性はどの程度かを答えます。
RMM管理下のフリート向けのオープンソース、単一スクリプトのCVEスキャナー。純粋なPowerShell 7で、エージェント不要、アプライアンス不要、ライセンス料無料。
管理下のすべてのエンドポイント上のすべてのソフトウェアに対して、1つの質問に答えます:インストールされているバージョンが脆弱であることが知られているか、そしてその緊急度はどの程度か? — RMMのソフトウェアインベントリを、無料で信頼できるセキュリティフィードと結合することで実現します:
出力:デバイスごとのCSVレポート、SLAクロックと週次変更イベントを含むSQLite履歴、自己完結型のHTMLダッシュボード、オプションのMarkdown公開レポート。
v1.0.0. エンジンは、Windowsフリートに対して本番環境で週次実行されていたスキャナーからクリーンルームで移植されました(docs/HISTORY.md参照)。このリリースの前に、2つを同一のライブフリートに対して実行し、行ごとに比較しました:両方が同一の*(デバイス、ソフトウェア、バージョン)*を確認した数万の検出結果にわたって、すべての計算フィールド — ステータス、CVSS、深刻度、KEVフラグ、SSVC判断、修正バージョン — が完全に一致しました。
この比較は判定ロジックをカバーしています。履歴/SQLレイヤーはテストされておらず、お客様の環境でツールを検証する代わりにはなりません。信頼する前にdocs/known-limitations.mdをお読みください — これはお世辞ではなく正直に書かれています。
PowerShell 7(pwsh)が必要です。2つの実行方法:
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
インベントリをCSVにエクスポートします(列:hostname, software, version、オプションでdevice_id, os)。その後:
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を参照してください。
brew install powershell; Windows: winget install Microsoft.PowerShell; Debian/Ubuntu: Microsoftパッケージリポジトリ(packages.microsoft.com)からインストール。brew install sqlite; Debian/Ubuntu: apt-get install sqlite3; Windows: SQLiteの「tools」バンドルをsqlite.orgからダウンロードし、sqlite3.exe を PATH に置くか、スクリプトと同じディレクトリに置く。これがない場合でもスキャンは実行されますが、first_seen は実行日にフォールバックします。first_seen 追跡を有効にします。ない場合 = それらの機能はスキップされ、スキャンは完了します。epss.empiricalsecurity.com(EPSSバルクスコアホスト — FIRSTがEPSSデータホスティングをそこに移行)、MSRC、endoflife.date、GitHub raw(CVE Program cvelistV5) — さらにライブモードではNinjaOne API。設定はJSONファイルです(デフォルトはスクリプトと同じディレクトリの config.json。-ConfigPath で上書き可能)。config.example.json から始めてください。スキャナーが読み取るすべてのキー、省略時のデフォルト値、その機能:
ninjaone(ライブモード)nvd| キー | デフォルト | 目的 |
|---|---|---|
nvd.api_key | "" | NVD APIキー。apiKey ヘッダーとして送信されます。空白の場合は未認証で実行され、nvd_rate_limit と nvd_min_spacing_ms のデフォルトがNVDの匿名上限(4/30秒、6500ms)に低下します(認証済みの場合は48/30秒、700ms)。無料で、約10倍高速です。 |
nvd_min_spacing_ms、nvd_rate_limit、および parallel_throttle はスキャナーによって読み取られますが、config.example.json には含まれていません — チューニングが必要な場合のみ追加してください。docs/rate-limiting.md を参照してください。
output以下はすべて 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 — サポート終了ファミリーが検出された場合に書き込まれます。列セットは固定されており、ゲートで強制されます。順に:
Run-WeeklyScan.ps1 はスケジュール実行用のラッパーです:自身のディレクトリからスキャナーを実行し、すべての出力ストリームを <スクリプトディレクトリ>/logs/ の下の日付付きログにキャプチャし(-LogDir で上書き可能)、スキャナーの終了コードを伝搬してスケジューラーが失敗を認識できるようにします。
Windows タスクスケジューラ:
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):
0 22 * * 0 pwsh -File /path/to/Run-WeeklyScan.ps1
cve-dashboard.html は完全に自己完結型です — 直接開くか、任意の静的ホストまたはファイル共有に配置するか、output.dashboard_share を使用して各実行後に自動的にコピーできます。アクセス制御されたホスティングには、dashboard-auth/ ディレクトリにMicrosoft Entra(Azure AD)認証付きの最小限のASP.NETホストが同梱されています。そのREADMEとRUNBOOKを参照してください。
-InputCsv モードにより、任意のRMM/SCCM/Intuneエクスポートをフィードできますが、本番環境で実行されたのはNinjaOneアダプターのみです。完全で率直なリスト — CPEスキップ率、重複キーのマスキング、page-1の totalResults、MSRCの優先順位の隙間など — はdocs/known-limitations.mdにあります。
一見型破りに見えるいくつかの構造上の選択は意図的です:
ForEach-Object -Parallel 内で実行され、それらの実行スペースは外側のスコープの関数を見ることができません — そのため、いくつかのヘルパーが並列ブロック内で意図的に重複しています。1ファイルに1関数のモジュールレイアウトでは、その重複は除去されません(並列ボディは依然として実行スペースごとにソースを注入する必要があります)。ビルドステップが追加され、1ファイルコピーのデプロイモデルが壊れるだけです。プロジェクトがいつか単一ファイルを超える場合、パッケージ化はv2の形の変更であり、パッチではありません。[PSCustomObject] レコード、クラスではない。 発見レコードは並列実行スペース内で生成され、メインの実行スペースにマーシャリングされます。PowerShellクラスインスタンスはその境界を確実に超えられません。[PSCustomObject] はそれを超えられ、CSV列契約は型システムではなくリリースゲートによって強制されます。tests/ 内のスイートは、終了コードアサーションを持つ依存関係のないスクリプトであり、pwsh が存在する任意の場所(スキャンサーバーを含む)で、何もインストールせずに実行可能です。CIはWindowsとUbuntuの両方ですべてを実行します。PR歓迎 — 特にRMMアダプター。基本ルールと安全な方向性の要件(ステータスロジックは「より多くの」可視性に向かってのみ誤る可能性があり、決して黙ってPATCHEDに向かってはならない)についてはCONTRIBUTING.mdを参照してください。
このツールは、結合する無料で信頼できるフィードと同じくらい有用です。それぞれがスキャン時にクエリされます。これに基づいて構築する場合は、それぞれの利用規約を尊重してください。
cvelistV5 — CVEレコードと、CISA ADPによって提供されるSSVC/vulnrichmentデータ。CVE®はMITRE Corporationの登録商標です。https://github.com/CVEProject/cvelistV5Apache-2.0 — LICENSEを参照。
| キー | デフォルト | 目的 |
|---|
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)。 |
| キー | デフォルト | 目的 |
|---|
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レコードフェッチ数。残りは次回実行に延期されます。 |
| キー | デフォルト | 目的 |
|---|
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.report_dir | (必須) | すべての出力 — CSV、履歴DB、ダッシュボード、キャッシュ — のディレクトリ。 |
output.wiki_security_page | "" | オプションのMarkdown公開ページへのパス。設定されている場合のみ書き込まれます。 |
output.dashboard_share | "" | 各実行後にダッシュボードのコピーを受け取るオプションのパス/共有。空の場合はローカルのみ。 |
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対象)。output.wiki_security_page に、設定されている場合のみ。| # | 列 | 意味 |
|---|
| 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が違反されているかどうか。 |