一个轻量开源、单脚本的CVE扫描器,专为RMM管理的设备集群而设计。纯PowerShell 7,无需代理、无需设备、无需许可费用。
对于您管理的每台终端上的每款软件,它只回答一个问题:已安装的版本是否已知存在漏洞,以及紧急程度如何? —— 通过将RMM的软件清单与免费、权威的安全数据源进行关联:
输出:每设备的CSV报告、带SLA时钟和逐周变化事件的SQLite历史记录、自包含的HTML Dashboard,以及可选的Markdown暴露报告。
v1.0.0。 引擎已从曾在生产环境中针对Windows设备集群每周运行的扫描器以干净室方式移植(参见 docs/HISTORY.md)。在此版本发布之前,两者针对同一生产设备集群运行并逐行比较:在数万个两者都看到相同(设备, 软件, 版本)的发现中,所有计算字段——状态、CVSS、严重性、KEV标志、SSVC决策、修复版本——完全一致。
该比较覆盖了判定逻辑。它并未测试历史/SLA层,也不能替代在您自己的环境中验证该工具。在信任它之前,请阅读 docs/known-limitations.md —— 它诚实而非奉承。
您需要 PowerShell 7(pwsh)。有两种运行方式:
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
将您的清单导出为包含列 hostname, software, version(可选 device_id, os)的CSV,然后:
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.org下载SQLite “tools” 包,并将 sqlite3.exe 放在 PATH 或脚本旁边。没有它扫描仍会运行;first_seen 将回退为运行日期。first_seen 跟踪。缺失 = 这些功能跳过,扫描仍完成。epss.empiricalsecurity.com(EPSS批量评分主机——FIRST已将EPSS数据托管移至此处)、MSRC、endoflife.date和GitHub原始内容(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/30s、6500ms),而不是认证后的上限(48/30s、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 Dashboard(图表库内联,无外部请求)。可选复制到 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/ 目录提供了一个最小的ASP.NET主机,集成Microsoft Entra(Azure AD)认证;详见其 README 和 RUNBOOK。
-InputCsv 模式允许任何RMM/SCCM/Intune导出来提供数据,但只有NinjaOne适配器在生产中运行过。完整的、不加粉饰的清单——CPE跳过率、去重键掩码、第一页 totalResults、MSRC取代漏洞、以及更多——在 docs/known-limitations.md 中。
一些看似非常规的结构性选择是有意为之的:
ForEach-Object -Parallel 内部运行,而这些运行空间无法看到外部作用域的函数——这就是为什么在并行块内有意重复了少数辅助函数。按函数拆分的模块布局并不会消除这种重复(并行主体仍然需要在每个运行空间中注入源码);它只会增加一个构建步骤并破坏“复制一个文件即可部署”的模式。如果项目将来超出单文件范围,打包将是v2形状的变更,而不是补丁。[PSCustomObject] 记录,而非类。 发现记录在并行运行空间内产生,然后封送回主运行空间。PowerShell 类实例无法可靠地穿越该边界;[PSCustomObject] 可以,并且CSV列合同通过发布门禁而非类型系统来强制。tests/ 中的套件是无依赖的脚本,带有退出码断言,可在任何有 pwsh 的地方运行——包括扫描服务器——无需安装任何东西。CI在Windows和Ubuntu上运行所有这些脚本。欢迎PR——尤其是RMM适配器。参见 CONTRIBUTING.md 了解基本规则和安全方向要求(状态逻辑只能朝着 更多 可见性方向出错,绝不能无声地朝向 PATCHED)。
该工具的有效性依赖于它所关联的免费、权威数据源。每次扫描都会查询以下每个来源;如果您在此基础上构建,请尊重它们的条款。
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 | 用于报告为 VULNERABLE(有漏洞)的CVE的最低CVSS基本评分。KEV列表中的CVE绕过此下限。 |
ssvc_mission_prevalence | high | SSVC 任务普遍性利益相关方输入(low / medium / high)。 |
ssvc_public_wellbeing | high | SSVC 公共福祉利益相关方输入(low / medium / high)。 |
| 键 | 默认值 | 用途 |
|---|
nvd_cache_ttl_days | 7 | NVD结果(按软件名称)在重新查询前缓存的天数。 |
nvd_cache_flush_every | 500 | 每完成 N 项时将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丰富化的最大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、历史数据库、Dashboard和缓存。 |
output.wiki_security_page | "" | 可选的Markdown暴露页面路径。仅当设置时才会写入。 |
output.dashboard_share | "" | 可选路径/共享位置,每次运行时会复制一份Dashboard到此处。留空为仅本地。 |
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 | 当微软的 UNKNOWN 被翻转为 PATCHED 时的证据。 |
| 24 | triage_status | 来自 triage.json 的分诊处置(若无则为空)。 |
| 25 | triage_note | 分诊备注。 |
| 26 | first_seen | 发现项首次被观察到的时间(来自历史数据库)。 |
| 27 | sla_due | SLA截止日期。 |
| 28 | sla_breach | SLA是否已违反。 |