alvidul/vulnrank

GitHub: alvidul/vulnrank

一款零依赖的本地终端漏洞与安全态势扫描器,基于 KEV、EPSS 和 CVSS 对漏洞发现进行真实风险优先级排序而非单纯按严重程度排列。

Stars: 0 | Forks: 0

# vulnrank [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/aa1ad677a6094957.svg)](https://github.com/alvidul/vulnrank/actions/workflows/ci.yml) [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Dependencies: none](https://img.shields.io/badge/runtime%20deps-0-brightgreen)](pyproject.toml) **扫描您本机的易受攻击包和高影响的本地安全态势风险, 然后在实时本地应用中对它们进行分流。** ``` git clone https://github.com/alvidul/vulnrank cd vulnrank run-vulnrank.cmd # Windows: creates .venv-win, installs, opens the local app # 或者,从任何 Python 3.11+ 环境中: python -m pip install -e . python -m vulnrank ``` 这会启动一个本地 Web 应用(地址为 `http://127.0.0.1:8765`)并在您的浏览器中打开它。 它会盘点受支持的本地包生态系统 - **Python、npm、OS、Ruby 和 Rust 包** - 在实时的 [OSV.dev](https://osv.dev) 数据库中查询它们,并根据现实世界的风险对匹配项进行排序。它还会检查本地安全态势信号,例如被禁用的 Windows 安全控制、具有风险的浏览器扩展权限,以及可写的 PATH 目录。 您可以观看实时日志中发生的扫描过程,随时点击 **Rescan**,并获得一份**修复计划**,告诉您哪些操作能消除最多的风险。 大多数积压工作是按 CVSS 严重程度排序的,这会将攻击者实际使用的那少数发现埋没在数以千计的理论上的严重问题之下。vulnrank 将 **CISA KEV 状态**(是否正被利用?)和 **EPSS 概率**(被利用的可能性有多大?)的权重置于基础严重程度之上。KEV 列表上的 CVSS 7.0 排名高于无人利用的 CVSS 9.8。这就是关键所在。 ## 功能 - **单条命令扫描机器**:`vulnrank` 会盘点 Python (PyPI)、npm、OS (dpkg/apk)、Ruby (gems) 和 Rust (cargo) 包,并通过 OSV.dev 发现它们的已知漏洞 - 无需安装 Trivy/Grype/Nessus - **本地安全态势检查**:Windows 防火墙、Defender、UAC、RDP、SMBv1、BitLocker、高风险浏览器扩展权限以及可写的 POSIX PATH 目录都会在本地进行检查,并清晰地标记为安全态势发现 - **实时本地应用**:带有 **Rescan** 按钮和流式扫描日志的服务器,让您可以按需确切地看到它检查了什么。仅绑定到 `127.0.0.1` - **修复计划**:将可修复的发现分组为具体的升级命令,并告诉您“修复这 N 个包可消除 X% 的风险”,同时支持复制到剪贴板 - **P1-P4 运维队列**:将风险评分转化为对管理者友好的优先级,包括实时应用中的 P1/P2 过滤器以及 CSV/JSON 导出中的 `priority` - **自上次扫描以来的变化**:快照会被存储,因此应用会显示新增与已解决的发现 ID 以及风险趋势 - **基于风险的排序**:KEV + EPSS + CVSS + 资产关键性评分;当仅发布向量时,会根据向量计算 CVSS 基础分数 - **SLA 时钟**:按严重程度划分的修复窗口,包含到期日和距离违规的天数 - **实时扩充**:OSV.dev、CISA KEV 和 FIRST EPSS 会被缓存;离线模式使用缓存的证据,如果缺少所需的缓存则会快速失败 - **同时支持导入扫描器文件**:传入 Trivy/Grype JSON 或 Nessus `.nessus` XML,转而对这些内容进行排序 - **结构安全**:不受信任的包文本在所有地方都进行了 HTML 转义;应用以 JSON 格式提供数据(无注入);具备 CSV 公式注入防御 - **零运行时依赖**:仅使用 Python 3.11+ 标准库 ## 本地覆盖范围契约 vulnrank 报告它可以从本地证据中证明的包漏洞和安全态势风险。 目前这意味着: | 本机上的来源 | 评估方式 | |---|---| | 运行 VulnRank 的环境中的 Python 包,以及位于 venv 内时的基础解释器元数据 | OSV PyPI 公告 | | 全局 npm 依赖树 | OSV npm 公告 | | Linux dpkg/apk 包 | 识别发行版时的 OSV Debian、Ubuntu 和 Alpine 公告 | | 本地 Ruby gems | OSV RubyGems 公告 | | Cargo 安装的 Rust crates | OSV crates.io 公告 | | Windows 防火墙、Defender、UAC、RDP、SMBv1 和 BitLocker 状态 | 带有修复指导的本地安全态势发现 | | Chrome、Edge、Brave 和 Firefox 扩展权限 | 针对广泛/敏感权限的本地安全态势发现 | | POSIX PATH 目录权限 | 针对组/全局可写命令路径的本地安全态势发现 | | 命令行传入的 Trivy、Grype 和 Nessus 文件 | 与本地扫描一起进行解析和风险排序 | 它**不**声称目前在固件、驱动程序、Windows 更新状态、没有包元数据的任意桌面应用程序、网络暴露或完整的 CIS 基准审计方面实现端点全覆盖。这些都是路线图项目,而不是隐藏的假设。 ## 扫描机器 ``` run-vulnrank.cmd # Windows one-command launcher python -m pip install -e . python -m vulnrank # live app: scan + dashboard + Rescan in the browser vulnrank # same command after install vulnrank --port 9000 # use a different port vulnrank --no-os --no-npm # only some ecosystems vulnrank --no-posture # package vulnerabilities only vulnrank -o report.html # one-shot: write a static HTML report instead of serving vulnrank -o queue.json # one-shot: write machine findings as JSON ``` 在应用中,发现结果按风险从高到低排序;使用搜索框和 **P1 / P2 / Exploited / Has fix / Critical** 以及各个生态系统的标签来筛选出重要内容,然后自上而下执行**修复计划**。 ## 对现有扫描器报告进行排序 vulnrank 仍然支持导入其他扫描器的输出 - 传入文件,它就会对这些内容进行排序,而不是扫描机器: ``` # 内置示例数据 + seeded cache,无需网络 vulnrank examples/trivy_scan.json examples/grype_scan.json \ --inventory examples/assets.csv \ --cache-dir examples/cache --offline \ -o queue.csv ``` ``` # PRI SCORE KEV EPSS CVSS CRIT FIX ID / PACKAGE / ASSET 1 P1 0.9760 yes 0.9200 10.0 5 no CVE-2024-3094 xz-utils@5.6.0 [web-prod-01] 2 P1 0.9140 yes 0.8800 7.5 5 yes CVE-2023-44487 nghttp2@1.52.0 [web-prod-01] 3 P3 0.2660 no 0.0000 8.3 5 yes CVE-2022-1471 snakeyaml@1.32 [web-prod-01] 4 P3 0.2560 no 0.0400 7.2 5 yes CVE-2021-23337 lodash@4.17.20 [web-prod-01] ``` 像分流主管那样解读最上面的那一行:在核心资产主机上发现的一个确认的、被积极利用的后门(xz)浮升到了第 1 名,而一个没人利用的、CVSS 更高的反序列化 bug(snakeyaml,8.3)则沉到了它下面。`FIX` 列标示了是否存在修复版本 - 请注意,排名第一的后门没有干净的前向修复(它需要降级),这正是那种需要补偿控制而非简单升级的发现。 面对实时推送的数据源时,去掉 `--offline` 和 `--cache-dir`: ``` vulnrank scan.json --inventory assets.csv -o queue.csv ``` `--top N` 控制打印到终端的行数;`--refresh` 强制重新获取 EPSS,而不考虑缓存时间。使用 `--min-score S`、`--kev-only` 或 `--fixable-only` 缩小队列范围 - 这些过滤器同时适用于终端预览和 `-o` 文件: ``` # 本周可采取行动的、被利用的工作:在 KEV 上,补丁可用,位于真实资产上 vulnrank scan.json --inventory assets.csv --kev-only --fixable-only -o sprint.csv ``` `-o` 文件格式遵循其扩展名 - `.csv`、`.json` 或 `.html`,用于生成可以通过电子邮件发送或附加到工单的独立报告。这也适用于机器扫描器,因此 `vulnrank -o queue.csv` 会扫描本地资产并生成一个队列文件: ``` vulnrank scan.json --inventory assets.csv -o report.html ``` ## 修复 SLA 每个发现都根据其严重程度级别(默认为 Critical 7d,High 30d,Medium 90d,Low 180d)获得一个修复窗口。到期日是发现日期加上该窗口期,而距离违规的天数从今天开始倒计时: ``` # 发现结果已超过其 SLA,鉴于这些首次被发现于 2026-05-01 vulnrank scan.json --discovered 2026-05-01 --overdue-only -o overdue.html ``` 通过 `--config` 文件中的 `[sla]` 表覆盖这些窗口(参见 `examples/weights.toml`)。`due_date`、`sla_days` 和 `days_to_breach` 会出现在 CSV/JSON 输出中,并在 HTML 报告中显示为颜色编码的 **Due** 列。 ## 评分 ``` score = w_kev * kev + w_epss * epss + w_cvss * (cvss / 10) + w_asset * (criticality - 1) / 4 ``` 每一项都被归一化到 0..1,因此最终得分是一个干净的 0..1 风险数值,而权重是唯一的调节旋钮。当扫描器未报告 CVSS 时,vulnrank 会回退到 CVSS v2 基础分数,然后再回退到根据严重程度标签推导出的最低分值,因此一个发现永远不会被悄悄埋没在零分。修复被刻意排除在风险评分之外 - 存在修复并不会使漏洞风险更高 - 而是作为打破平局的手段,以便在同等风险的发现中,可修补的发现优先浮现。 | 信号 | 默认权重 | 原因 | |---|---|---| | CISA KEV | 0.40 | 在野被确认利用,是目前存在的最强信号 | | EPSS | 0.30 | 预测的利用概率涵盖了 KEV 尚未捕捉到的内容 | | CVSS | 0.20 | 理论上的严重性依然重要,只是不应起主导作用 | | 资产关键性 | 0.10 | 核心资产上的发现胜过实验室机器上的相同发现 | 通过 `--config examples/weights.toml` 进行覆盖。权重总和必须为 1.0。 资产关键性来自可选的 CSV(`asset,criticality`,1 到 5)。未列出的资产默认为 1,因此空的资产清单会降级为纯威胁信号排序。 ## 工作原理 ``` parse (local packages/posture or Trivy/Grype/Nessus) -> dedupe -> enrich CVEs -> score + sort ``` | 模块 | 职责 | |---|---| | `localscan.py` | 枚举受支持的已安装包生态系统 | | `posture.py` | 在不将主机详细信息发送给服务的情况下证明本地安全态势风险 | | `ingest.py` | 自动检测格式,将每个扫描器解析为一个扁平的 `Finding` 列表 | | `pipeline.py` | 跨扫描器去重,加载资产清单 + 权重,计算得分 | | `enrich.py` | 获取并缓存 CISA KEV + FIRST EPSS,遵循离线和新颖性规则 | | `cli.py` | 参数解析、终端预览、CSV/JSON 导出 | | `models.py` | `Finding` 和 `ScoredFinding` 数据类 | ### 设计决策 - **威胁证据重于严重性。** 四个默认权重中有两个(KEV、EPSS)是利用信号;CVSS 被刻意限制在 0.20。排名反映的是正在受到攻击的内容,而不是理论上可能很糟糕的内容。 - **新颖性是明确的,绝不静默。** KEV 在每次在线运行时都会重新获取;EPSS 是每天重新发布的概率,因此一旦缓存的 EPSS 值满一天(或在触发 `--refresh` 时)就会刷新。离线模式绝不接触网络,并在缺少缓存时快速失败,而不是悄悄地将所有内容评分为零。 - **能在真实扫描器下存活的去重。** Trivy 和 Grype 对同一个镜像的描述方式不同(`ArtifactName` vs `source.target.userInput`);解析器会对资产进行规范化,使得来自这两个工具的相同 CVE 合并为一行排序记录,并同时记录两个来源。 - **风险和可操作性是独立的两个维度。** 四个加权信号回答“这有多危险?”;修复的可用性回答“我能采取行动吗?”。将修复纳入风险评分会扭曲它,因此修复可用性被记录为一个字段,仅在同等风险的发现中作为打破平局的手段使用。 - **安全态势发现被标记为安全态势发现。** 被禁用的防火墙很重要,但它不是 OSV CVE。VulnRank 保持该来源可见,同时仍然将其放入响应人员所使用的同一个 P1-P4 队列中。 - **不受信任的输入始终保持不受信任。** 包名和 CVE 文本来自扫描器并流入电子表格,因此 CSV 输出会中和公式注入 payload。 ## 输入格式 | 扫描器 | 生成兼容输出的命令 / 导出操作 | |---|---| | Trivy | `trivy image --format json -o scan.json ` | | Grype | `grype -o json > scan.json` | | Nessus | 从 Nessus UI 将扫描导出为 `.nessus` (XML) | 格式会根据每个文件自动检测(JSON vs `.nessus` XML),因此可以在一次运行中传入任意组合。Nessus 的发现是基于主机的,因此它们不携带包信息;它们依然会根据 CVE + 资产进行去重和排序。 ## 开发 ``` pip install -e ".[dev]" ruff check . mypy pytest -q ``` CI 会在 Python 3.11 和 3.12 环境下,针对每次推送和拉取请求运行 ruff(代码检查)、mypy(类型检查)和测试套件 - 并带有 90% 的覆盖率门槛。带有标签的 GitHub Release 会通过受信任的发布机制构建并发布到 PyPI。 有关约定请参阅 [CONTRIBUTING.md](CONTRIBUTING.md),报告漏洞请参阅 [SECURITY.md](SECURITY.md)。 ## 路线图 - SBOM (CycloneDX) + VEX 感知 - 针对 Lockfile 和容器的扫描,以覆盖项目依赖 - Windows 更新/CVE 关联以及更广泛的基准检查 ## 许可证 MIT - 参见 [LICENSE](LICENSE)。
标签:API接口, Python, XXE攻击, 无后门, 漏洞优先级评估, 系统安全基线检查, 终端安全, 逆向工具, 配置审计