每份 VAPT 报告的结尾都如出一辙:只有寥寥几个数字。一个 CVSS 分数。一个严重性标签。一个优先级排名。有时会有一个综合风险分数。这些正是修复团队实际采取行动的数字:本次冲刺会修复什么,什么会被推迟。
当大型语言模型进入该流水线(编写摘要、解释发现、起草修复步骤)时,一个更为安静的架构问题随之而来:该模型只是在复述已有的分数,还是在某个环节实际上在塑造这些分数?
这是一篇技术说明,阐述 ONUS——一个开源、自托管的 DAST(动态应用安全测试)平台——如何通过设计而非政策来回答这个问题。内容来源于 ONUS 的内部项目报告、其架构、其测试套件以及在开发和验证期间收集的扫描数据,测试数量已针对实时仓库进行独立验证(详见下文),而非来自 GitHub 星标、扫描次数或其在 OWASP 漏洞扫描工具目录中的列表。这些都不能证明架构是可靠的,本文故意将它们排除在外,以突出实际构建、测试和观察到的内容。仓库、项目站点以及该列表的链接位于文末,仅作参考,而非作为任何证明。
ONUS 的自身验证表明该设计在实践中成立:每个得分字段都可以追溯到单一函数,因此在未变更的目标上重新扫描会产生完全相同的数字。当本地语言模型响应缓慢或不可达时,只有报告的散文部分会受到影响。严重性、CVSS 和优先级数字永不会变动。
ONUS 简介
我在开源之前,曾在 IIT 坎普尔计算机中心,在 Navpreet Singh 的指导下,将 ONUS 构建并验证为一个受监督的项目。
对于首次接触 ONUS 的读者:ONUS 是一个自托管的 DAST 平台。操作员提交一个授权域名;八个扫描模块并行针对其运行;结果会进行去重、被动再验证,并依据官方 CVSS v3.1 公式(行业标准的 0–10 漏洞严重性评分尺度)进行评分,由本地语言模型用纯英文描述,最后以可下载的 PDF 和实时仪表盘两种形式交付。
整个系统贯彻三项承诺:
- 设计即气隔。 扫描数据或发现永不会离开本地网络,包括 AI 组件——它是在本地硬件上运行的 Ollama(Qwen 2.5 7B),而非托管 API。
- 零许可成本。 它所编排的每个扫描工具(nmap、OWASP ZAP、Nikto、testssl.sh、Nuclei、FFUF、WhatWeb、WAFW00F、subfinder、Amass)均为开源。
- 设计即无破坏。 主动测试仅使用只读的概念验证有效载荷。
那种架构其实不是本文的主题。真正的主题是:ONUS 为其 LLM 所能触及的内容划定的那条线,以及在这条线的两侧,当事情出错时会发生什么。
核心设计问题:LLM 被允许决定什么?
ONUS 的项目报告将这一点列为其核心设计目标之一,用直白的话说:最终报告中的每一个数值得分(CVSS 分数、CVSS 向量、严重性、优先级、聚合风险得分)都必须来源于确定性公式,而绝不能来自语言模型,以至于对同一次扫描运行两次会得到完全相同的字节数值。
这是一个强烈的主张。我们值得探究为什么这是正确的,而不仅仅是记录 ONUS 如此主张。
可重复性是可以测试的;而“通常正确”则不然。 纯函数(cvss_scorer.py::score_finding())可以在单元测试中针对已知的 CVSS 向量进行检查,并断言永不漂移。ONUS 的测试套件(截至目前有 690 项测试)正是如此:CVSS 公式会针对已知的向量进行验证,作为一种具体且持续的测试类别。你无法编写等效的测试来验证“模型将此发现评为中等”,因为模型的输出会随着提示、模型版本以及(对于 ONUS 用来保持离线的自托管较小模型,如 7B 版本)当天是否甚至能够连接而变化。
可用性和正确性不应共享同一种失效模式。 如果 LLM 负责生成严重性数字,LLM 停机将迫使我们做出选择:要么完全阻止报告,要么悄悄回退到第二条、经过较少测试的评分路径——恰恰是这条较少测试的路径最危险的地方。ONUS 自身的结果讨论证实了解耦在实践中得以保持:Ollama 实例若变慢或无法访问,只会降低报告的文字质量,而不会影响其严重性、CVSS 或优先级数字。在重试后 Ollama 仍不可达时,会使用基于规则的回退描述进行替换,并将报告明确标记为(ai_unavailable),决不会沉默地留空,也不会走向不同的评分路径。
模型在评分之前永远不会看到证据。 这是结构性的,而不仅仅是政策:聚合、被动再验证以及 CVSS 评分全部会在调用 LLM 之前发生并完成。当 Qwen 2.5 7B 看到一个发现时,它已经完成去重,已经按置信度分层,并且已经携带了最终的 CVSS 分数和优先级。此后,它的唯一任务就是对其进行描述。
这一点还有一个更为普遍的好处值得提及,尽管报告本身并未提出或旨在验证这一点:扫描器的原始证据常常包含直接从目标提取的文本:页面标题、错误字符串、响应体。这是与攻击者相关的内容,有时甚至可被攻击者控制。一个既读取目标原始内容 并 分配严重性评分的模型,其评分原则上可能被足够有动机的目标尝试影响。如果证据中出现对抗性内容,仅仅撰写关于其无法改变的数字的散文的模型,其影响范围会小得多。先评分后描述在结构上关闭了这扇门,无论这是否是设计的初衷。
系统架构
ONUS 是一个六层流水线。每层只负责一项工作,并与相邻层进行通信。
| 层 | 组件 | 职责 |
|---|---|---|
| 1: 输入 | Next.js frontend | 域名输入表单、授权复选框、实时扫描状态 |
| 2: 后端 | FastAPI + PostgreSQL | 请求验证、作业创建、状态 API、报告交付 |
| 3: 队列 | Celery + Redis | 异步调度、并行工作器编排、任务状态 |
| 4: 扫描 | 8 Python modules | 执行外部工具,将输出规范化为共享的 JSON 模式 |
| 5: 智能 | Ollama + Qwen 2.5 7B | CVSS 评分、风险排名、修复建议文案 |
| 6: 输出 | WeasyPrint + Next.js | PDF 报告、交互式漏洞仪表盘 |
第 5 层的标签是一种简化:它所来源的架构图只能容纳一个框。第 5 层实际上是四个顺序阶段,只有最后一个阶段会涉及 Ollama。这就是下面《Inside the Analysis Pipeline》所讨论的内容。
当操作员提交一个已获授权的域名时,扫描开始。FastAPI 验证请求,直接拒绝私有 IP 范围(RFC 1918)和 localhost,检查是否存在针对同一域名的重复或并发扫描,创建一个 Scan 记录,并将任务推送到 Redis。Celery 调度一组八个并行扫描子任务;当所有八个任务均完成后,和弦回调(一个在组内每个任务完成时触发的 Celery 原语)被触发,此后才开始进行聚合、验证、评分、描述和 PDF 渲染。
有两个 schema 级别的选择值得指出,因为它们表明分层是在数据库中强制执行的,而不仅仅是在图中。生成的 PDF 被存储在一个单独的 reports 表中(作为 BYTEA),刻意与 scans 行分开,以便轮询扫描状态时永不需要读取或写入二进制 PDF 数据。并且,对扫描的每个模块状态映射的更新使用原子的原始 jsonb_set SQL 语句,而不是读取-修改-写入的 ORM 更新,具体是为了避免多个并行的 Celery 工作进程同时尝试更新同一扫描的状态时出现竞争条件。(值得注意的是,WeasyPrint 也用于渲染 ONUS 自身的底层项目报告:相同的渲染器,两个任务。)
八个模块,一个模式
| 序号 | 模块 | 工具 | 发现 |
|---|---|---|---|
| 1 | 侦察 | nmap, subfinder, Amass, httpx, Naabu, WHOIS, dnspython | 端口/服务, 子域名, 活跃主机技术, WHOIS, DNS/SPF/DMARC/DKIM |
| 2 | Web 扫描 | OWASP ZAP, Nikto, Katana | XSS/SQLi/CSRF/身份验证漏洞, 配置错误, 具备 JS 感知的端点 |
| 3 | SSL/TLS | testssl.sh, sslscan | 协议/密码问题, 证书有效性, HSTS |
| 4 | HTTP 头部 | 纯粹的 requests |
CSP/HSTS/X-Frame-Options/CORS/cookie flags |
| 5 | OWASP Top 10 |
requests, 6 个测试函数 |
SQLi, XSS, IDOR, 路径遍历, 开放重定向, 错误泄露 |
| 6 | 技术指纹 | WhatWeb, WAFW00F | CMS/框架/服务器检测, WAF(Web 应用防火墙)存在 |
| 7 | Nuclei CVE | Nuclei | 已知 CVE, 配置错误, 暴露的面板 |
| 8 | 目录枚举 | FFUF | 暴露的文件, 管理面板, 受身份验证保护的路径 |
这些工具本身并没有什么新颖之处:nmap、ZAP、Nikto、testssl.sh 和 Nuclei 各自已经很好地完成了各自的工作。它们单独使用时无法做到的,而项目报告将其视为相较于单独运行它们的实际贡献,是:在一个协调的管道中对单一目标进行操作,对它们的重复发现进行去重和交叉引用,在所有工具上应用一致的公式导出得分,并生成一个非技术读者可以采取行动的单一叙述。
每个模块在受控超时下通过 subprocess 包装其外部工具,并且每个模块必须将其输出规范化为一个共享的发现 schema:module、tool、type、title、evidence、target、found_by 以及一个 confidence/verifiable 标志。该 schema 在整个代码库中被视为不可协商,原因很具体:如果一个模块输出了格式错误的发现,会在聚合阶段导致静默的数据丢失。记住这一点:它之后会再次出现,一次作为设计决策,一次作为实际的 bug。
登录后的扫描
对于位于身份验证后的目标,操作员可以在提交时提供登录凭据。ONUS 将它们存储在 Redis 中,以 scan ID 为键,既不会作为 Celery 任务参数,也不会写入到 scans 表。扫描模块会检索凭据,自动检测登录是 HTML 表单还是 JSON API,登录后仅对已认证的表面进行爬取和测试。明确排除登出形状的链接不参与爬取:这是一条因真实 bug 而存在的规则,将在下文说明。扫描结束后,凭据会从 Redis 中删除。
分析管道内部
当所有八个模块报告完成后,ONUS 会在任何内容到达人类或充当叙述者的 AI 模型之前,运行一个固定的四阶段管道。该管道(以及其阶段按固定顺序运行、每个阶段依赖于前一阶段已完成这一事实)正是本文所讨论的实际信任边界。
flowchart TD
A["8 module result envelopes"] --> B["Aggregator<br/>dedupe + fingerprint collapse"]
B --> C["Confidence Verifier<br/>passive re-observation only"]
C --> D["Deterministic CVSS Scorer<br/>CVSS v3.1 formula"]
D --> E{"Ollama reachable?"}
E -->|Yes| F["Ollama (Qwen 2.5 7B)<br/>description + remediation prose"]
E -->|No, after retries| G["Rule-based fallback<br/>ai_unavailable = true"]
F --> H["Merge: scores from D, prose from F/G"]
G --> H
H --> I["Scored + described findings"]
1. 聚合
聚合器会将多个模块上报的相同发现去重为单条记录,并将形状完全相同的大量响应合并为一条汇总的发现。这一点比听起来更重要:它是针对基于字典的目录暴力破解的防御,这种破解会返回数千条几乎相同的“发现”,从而淹没报告中的其他内容。
2. 置信度验证:被动且与评分严格分离
这是最值得放慢速度来关注的阶段,因为在更简单的设计中它最容易被跳过,而 ONUS 的报告明确指出它并未被跳过:置信度验证是一个独立的、独立的阶段,位于其自身的模块中(backend/analysis/verifier.py),严格处于聚合和评分之间。
其规则是绝对的:每个验证器会重新发出扫描模块已经发送的确切相同的非破坏性请求,并检查相同的证据是否仍能重现。它永远不会被赋予新的利用技术或有效负载,即使添加一个微不足道。正是这一约束防止“验证”悄然变成第二次未经授权的利用尝试。具体到反射型 XSS,这种重新观察发生在实际的浏览器中(Playwright 驱动的无头 Chromium),而不是原始的 HTTP 重放,因为确认反射型有效负载确实执行需要真实的渲染上下文,而不仅仅是响应体中的字符串匹配。
每个发现都属于以下三个层级之一:
| 层级 | 含义 | 发现如何进入该层级 |
|---|---|---|
| 已确认 | 已重新验证的证明,或无需进一步检查的信号(例如,直接在响应中返回的数据库错误字符串) | 验证器重新发出相同请求,且证据仍能重现 |
| 可能的 | 默认 | 尚未重新检查,或当前无法验证 |
| 未验证 | 无法再次证明 | 验证器运行后,原始证据未能重现 |
未能重现的发现不会被直接丢弃:它会被降级为 unverified,并记录原因。报告明确说明了原因:悄悄丢弃它会重新引入此阶段旨在防止的确切类别的数据丢失错误。
等级不仅仅是一个标签;它会确定性地改变两个方面:发现的优先级(已确认的发现会提升一个紧急程度,未验证的则降低一个紧急程度)以及它对总体风险评分的贡献。最终的 PDF 会按照等级(已确认、可能的、未验证)对发现目录进行分组,而不是混合在一起,这样读者就能一眼看出哪些漏洞已经得到重新验证,哪些仍需要人工审查。
3. 确定性 CVSS 评分
每个发现(现在同时具备去重后的身份和置信度等级)都会通过一个 CVSS v3.1 评分器,该评分器对 73 种不同的发现类型有明确的规则。这是本文开头所述可重复性声明所依赖的唯一功能。
4. LLM 描述与修复:永不触及数字的后备方案
只有在评分完成后,Ollama 才会看到这些发现,并且仅用于生成描述性文字和修复建议。如果 Ollama 不可达或在重试后超时,则会使用基于规则的回退描述进行替换,并相应地标记报告:决不留空,更重要的是,决不会重新路由到另一条评分路径。最后的合并步骤会将第三阶段的得分与两种描述来源中实际运行的那一种的散文结合起来。
工作示例:一次真实扫描
架构图让人很容易点头赞同,但实际想象起来却很困难。以上管道在故意设置漏洞的公开测试站点 testphp.vulnweb.com 上得到的结果,正如报告自身仪表盘截图所示。
| 发现项 | 严重性 | CVSS | OWASP 类别 | 模块 | 优先级 |
|---|---|---|---|---|---|
| 缺少 SPF 记录 | 中等 | 4.3 | A05:2021 – 安全配置错误 | RECON | 3 |
| 缺少 DMARC 记录 | 中等 | 4.3 | A05:2021 – 安全配置错误 | RECON | 3 |
| 未找到 DKIM 记录(常见选择器) | 中等 | 4.3 | A05:2021 – 安全配置错误 | RECON | 3 |
| nmap 未发现开放端口(或扫描超时) | 信息性 | 0.0 | N/A | RECON | 5 |
| 发现 A 记录 | 信息性 | 0.0 | N/A | RECON | 5 |
| 发现 TXT 记录 | 信息性 | 0.0 | N/A | RECON | 5 |
| 未在端口 443 检测到 HTTPS 服务 | 信息性 | 0.0 | N/A | SSL_TLS | 5 |
| 目标不可达,无法进行头部分析 | 信息性 | 0.0 | N/A | HEADERS | 5 |
| 未检测到 WAF | 信息性 | 0.0 | N/A | TECH_FINGERPRINT | 5 |
总体:4/100,低风险(0 个 Critical,0 个 High,3 个 Medium,0 个 Low,6 个 Informational)。
基于该表格,以下是 LLM 对同一次扫描的贡献:
对 testphp.vulnweb.com 的安全扫描揭示了与域名安全配置和网络可访问性相关的若干问题。该站点缺少基本的电子邮件身份验证记录(SPF、DMARC、DKIM),这可能导致钓鱼攻击和未被发现的垃圾邮件。此外,端口 443 上缺乏 HTTPS 服务以及未检测到 Web 应用防火墙,表明在数据防护和流量过滤方面存在潜在漏洞。总体而言,这些发现表明其安全态势处于中等水平,需要加以改进以抵御常见的网络威胁。
有两点值得注意。首先,这三个 Medium 发现都具有完全相同的 CVSS 分数(4.3):它们是同一种底层发现类型(缺少电子邮件身份验证记录),并且每次都由同一的确定性规则评分。这就是“重新运行时字节相同的数字”这一说法的具体体现:相同的发现类型、相同的分数,没有例外。其次,上面的段落是 唯一 该 LLM 在此输出中触及的部分。如果在此次运行期间 Ollama 不可达,表格将完全不变:只有该段落会被替换为通用回退文本并标记为如此。
运营弹性:扫描中途出现故障时
扫描的状态是一个小型状态机,有趣的设计决策全部取决于非正常路径上的情况。
stateDiagram-v2
[*] --> queued
queued --> running
running --> analysing: all 8 modules succeeded or partial
running --> awaiting_user_decision: a module failed or timed out
running --> failed: stuck-scan deadline exceeded
awaiting_user_decision --> running: operator retries failed modules
awaiting_user_decision --> analysing: operator continues without them
awaiting_user_decision --> cancelled: operator cancels
awaiting_user_decision --> failed: stuck-scan deadline exceeded
analysing --> complete
complete --> [*]
cancelled --> [*]
failed --> [*]
如果八个模块中的任何一个报告 failed 或 timeout,管道不会在没有它的情况下悄悄继续:它会在 awaiting_user_decision 处暂停,并将故障呈现给操作员,操作员可以选择重试失败的模块、在没有它们的情况下继续,或直接取消扫描。这种暂停是经过测试的、承载负载的状态,而不仅仅是图表上的一个框:重试/继续/取消端点是 690 测试单元套件的一部分。
另外,卡住扫描的收割器(reaper)会独立地将超过硬截止时间且无进展的任何扫描标记为失败。这可以防止一种特定且易于遗漏的失败模式:Celery 的硬时间限制会直接杀死任务,在它有机会报告为 failed 之前。如果没有收割器,该扫描将永远停留在 running 状态,而管道中没有任何东西能察觉出问题。
测试:三个层次,有意保持分离
ONUS 的报告将测试框架为三个不同的练习,特意与实现工作分开:
- 单元:690 测试,pytest,模拟数据库会话,运行时间为秒。 涵盖针对已知向量的 CVSS 评分公式、聚合器的去重和响应指纹折叠逻辑、置信验证器的通过/失败/降级行为、决策流端点(重试/继续/取消)、卡住扫描收割器以及 Scans-listing 端点的过滤/排序/搜索/分页行为。
- 集成:一个活的 Docker Compose 堆栈(PostgreSQL, Redis, ZAP, backend, worker),未模拟。 练习完整的 Celery 流水线:组调度,chord 回调,聚合,评分,Ollama 调用,PDF 生成。这一点故意未被模拟,专门为了捕捉仅在进程间边界出现的问题。正是这种方式,文章后面讨论的两个 bug 才被实际发现。
- 端到端和已认证验证。 通过实际前端进行的真实扫描,在真实浏览器(Playwright 驱动的无头 Chromium)中,针对九个 deliberately 脆弱的练习目标以及一个额外的授权公开目标进行。已认证扫描的验证与未认证扫描分开进行,因为它行使了一条不同的代码路径。
值得精确地说明这一点所确立的内容。所有三个层次都测试 管道 的行为是否正确:已知的 CVSS 向量应如何评分,失败的模块是否确实会暂停扫描,登录流程是否确实在爬取前完成身份验证。它们都不衡量底层扫描器在 ONUS 未见过的应用中正确识别真实漏洞的频率。这是一个不同且更难的问题,报告也不声称能回答它。几节后会进一步说明。
实际测量了什么
| 指标 | 数值 | 来源 |
|---|---|---|
| 自动化后端测试 | 690 |
pytest --collect-only, 实时仓库,2026年8月验证
|
| 扫描模块 | 8 | backend/tasks/ |
| 不同的 CVSS 评分发现类型 | 73 |
cvss_scorer.py的规则目录
|
| 积极映射的 OWASP Top 10 (2021) 类别 | 5 of 10 |
aggregator.py的类别映射
|
| 最大并发扫描数 | 5 (configurable) | config.MAX_CONCURRENT_SCANS |
| Docker Compose 服务 | 18 | docker-compose.yml |
代码总行数(后端 .py + 前端 .ts/.tsx) |
15,810 |
wc -l, 排除 node_modules/.next
|
| 开发/验证期间执行的扫描 | 79 | 实时 scans 表 |
| 开发/验证期间生成的 PDF 报告 | 124 | 实时 reports 表 |
| 使用的验证/实践目标 | 9 | docs/test_findings.md |
该表中的一个数字不是报告自身的数值,值得明确标出,而不是让它混在其它数字中:测试计数。报告称有 438 项自动化测试。在准备本文时,我克隆了实时仓库并直接运行 pytest --collect-only;它干净地收集了 690 项测试,没有错误。这就是本文后续所使用的数字。表格中其他所有数字均与报告所述完全一致,未作修改。
以下两点值得进一步解读,我已明确标注为我的解读,而非报告自身的框架:
- "OWASP Top 10 类别" 是一个部分标签,而非普遍标签。 只有 10 个官方 OWASP Top 10:2021 类别中的 5 个被聚合器积极映射。在上面的工作示例中可以直接看到:三个错误配置发现获得了 OWASP 标签;六个信息性发现没有。这是预期的行为,而不是错误。但 “OWASP 类别” 应该被理解为 “在存在规则的地方进行映射”,而不是声称完全覆盖 Top 10。
dvwa.local 的扫描在不同运行中最短为 0.9 分钟,最长为 5.8 分钟;clinkl.in 完成大约需要 4.6 分钟;nodegoat.local 在其两次完整运行中大约需要 13.5–14 分钟。给出一个“平均扫描时间”会是一个更有冲击力、更易引用的数字,但鉴于这种分散,它也会更具误导性。报告选择给出范围而不是平均值,这是一个值得注意的小方法学选择。
两个塑造架构的错误
实际验证发现了两类仅靠设计评审无法发现的错误,报告指出这两类错误,因为它们的影响超越了具体的修复。
导致 IDOR 检测失效的登出链接
为 NodeGoat 的 /allocations/:userId 漏洞构建的 IDOR(不安全直接对象引用)检测器在完整管道中运行时最初未发现任何问题,尽管在隔离测试时表现正常。根本原因:爬虫在爬取过程中跟随了 NodeGoat 自身的登出链接,静默地销毁了该运行中所有剩余测试的已认证会话,而不仅仅是碰巧触发登出的那个测试。
通过在爬取中排除形似登出的链接,此修复提升了针对已认证目标的所有 OWASP Top 10 测试,而不仅仅是 IDOR 检测。报告对这一可推广经验的阐述值得保持完整:一个“在隔离环境下工作”的检测器,尚未经过实际管道的验证,直到它在将要调用它的实际管道中运行时才算被验证。
未发出任何警告却失败的工具
此外,若干外部工具集成(testssl.sh、WHOIS 查询、WhatWeb)在不同阶段被证实为非功能状态,且未产生任何错误或空结果警告。它们仅仅从未贡献任何发现,静默地失败,原因从缺少系统包到错误的标志不等。
这可以说是两类错误中更令人担忧的一类,因为错误的输出至少可见;而既没有输出也没有错误则不然。这两类错误促使了相同的结构性应对:模块的执行状态——无论是未发现任何问题、彻底失败,还是静默成功——现在始终可见于报告中,并与前文所述不可妥协的发现模式关联。这直接追溯到两个具体的生产环境错误,指向一个特定的架构不变量:模块运行的任何方面都不允许保持沉默。
防护措施
系统每一层都贯穿着若干结构性防护措施:
| 防护项 | 机制 |
|---|---|
| 授权 | 每次扫描都需要显式的确认 authorized: true,并记录时间戳 |
| 网络隔离 | 请求验证阶段会拒绝私有 IP 范围(RFC 1918)和本地主机 |
| 无破坏性测试 | 主动测试仅使用只读的概念验证有效载荷:不修改数据,也不使用拒绝服务有效载荷 |
| 审计追踪 | 每次扫描(目标、时间戳、操作员)都会被永久记录 |
| 速率限制 | 可配置的并发扫描上限;对同一域名的重复主动扫描予以拒绝 |
| 数据隐私 | 零外部 API 调用:所有分析(包括 LLM)均在本地基础设施上运行 |
这一点尚未得到证明
报告对 ONUS 的功能范围持坦诚态度(参见下文的局限性部分)。同样有必要对其证据能够和不能够确立的内容保持坦诚,因为在任何项目撰写中这点很容易被模糊,本报告也不例外。
- 未给出检测准确率数据。 对于这八个模块或 OWASP Top 10 测试函数,均未报告误报率、漏报率以及精确率/召回率。690 个单元测试表明流水线的 逻辑 在已知输入下行为正确(包括 CVSS 公式、置信度转换、去重逻辑),而并未说明底层扫描器正确标记真实漏洞的频率。
这并非仅针对 ONUS 的批评:大多数 VAPT 报告,无论是开源还是商业的,也不发布精确率/召回率数据。但一篇旨在区分所测量内容与所设计内容的文章,应当持有相同的标准。
关键要点
- 将每个修复决策所依赖的数字(CVSS 分数、向量、严重性、优先级、累计风险)放在确定的、可单元测试的函数后面。永远不要放在模型调用后面。
- 为 LLM 指定一个狭窄且后置的任务:对已经完成评分的发现撰写说明文本,并在其不可达时提供一个带标记的确定性后备方案。
- 被动的、无害的重新验证(重新发出相同请求,永不使用新载荷)可以增加信任层级,而不会将扫描器变成第二次利用尝试。
- 绝不要静默丢弃未通过重新验证的发现。应给出原因将其降级,以免假阴性伪装成干净的扫描。
- 针对活栈(而非模拟)进行跨进程集成测试,发现了单元测试在结构上无法捕获的漏洞:注销链接在爬取过程中中断已认证的会话,工具在未 ever 抛出错误的情况下失败。
- 仅在隔离环境中能工作的组件,在未通过实际会调用它的管道运行之前,尚未得到验证。
局限性
以下内容直接摘自项目报告(报告中公开承认当前功能存在的差距,而这并不构成对确定性/LLM 边界本身的反证):
- 相邻 ID 的 IDOR 检测存在已知上限。 IDOR 检测器会探测附近的数字或类似 ObjectId 的标识符;它无法发现仅由非顺序、随机标识方案保护的 IDOR。
- 已认证扫描仅支持 HTML 表单和 JSON‑API 登录方式, 均可自动检测。多步骤或 OAuth 风格的登录流超出当前实现的范围。
- Ollama 的可用性是一种软依赖,这是设计如此。 即使无法连接,每个确定性字段仍会被计算,且回退方案会被明确标记,而不是静默降级。但描述和修复建议的质量确实会因本地模型在特定运行期间是否可达而有所不同。
- 扫描仪表盘的“全选”功能仅限于当前页面, 尚未接入批量操作:它是未来批量重试/取消操作的挂钩点,而非已交付的功能。
- 扫描仪表盘的按状态排序 按状态字母顺序进行,而非按照运营紧急程度排名。
- OWASP Top 10 类别标签目前仅覆盖官方 2021 年版的 10 类中的 5 类(参见 "实际测量了什么" 上文)。
未来研究方向
以下内容摘自报告自身的未来范围章节:
- 在扫描仪表盘现有的选择栏挂钩点上增加批量操作(重试/取消)。
- 向 IDOR 检测器提供第二个真实已认证用户的标识符,而不仅仅是猜测附近的值,以消除其相邻 ID 上限。
- 在已认证扫描中支持更多类型的登录流。
- 面向操作员的设置页面,用于配置 subfinder 的可选提供方 API 密钥(目前为挂载的配置文件),从而在不修改主机文件的情况下实现更深层的子域枚举。
除了报告中提出的内容之外,上述评估空白还指向该议程的一些自然补充:
- 在已标记且更多样化的目标集上发布精确率/召回率或误报率数据,而不仅仅是针对故意设置漏洞的练习应用。
- 对置信度验证器本身进行对抗性测试:故意设计表现不一致的目标。
- 对 LLM 生成的修复建议与回退方案进行结构化比较,由未参与生成流程的第三方进行评估。
资源
- 源代码: github.com/maverickaayush/ONUS
- 项目网站: tryonus.tech
- OWASP漏洞扫描工具目录: owasp.org/www-community/Vulnerability_Scanning_Tools