根本原因关联(专业版) (Pro)

一个被引入四十个服务的易受攻击库会产生四十个发现项。每一个都是真实的,每一个都被单独分类处理,而且每一个都可以通过同一次版本升级来修复。根本原因 关联功能将这种关系明确呈现出来:DefectDojo Pro 会将共享根本原因的发现项分组,形成一个按优先级排序的根本原因 列表,让您能够看到那一次修复以及它所清除的所有内容。

关联是增量式且非破坏性的。每个发现项都会保持独立可见,保留自身的状态,并且分类处理方式与以往完全相同。关联只会在发现项 之间添加链接,添加这些链接汇总而成的聚类节点,以及生成每条链接的证据。

关联不是去重。 去重 会判定两份报告描述的是同一个发现项,并将其中一个标记为重复项。关联则是将碰巧共享同一原因的不同发现项 关联起来,绝不会将任何内容标记为重复项。两者相互独立运行,可以同时启用。

启用根本原因关联

根本原因关联目前处于测试版阶段,由功能开关控制,且默认关闭。 超级用户可以在 Cloud 和本地部署实例上通过设置 > 功能开关将其启用。请参见 功能开关

该开关关闭时,引擎完全不会执行任何操作:不会构建任何聚类,不会创建任何链接, 导入后也不会分派任何任务。

启用该开关后,现有发现项不会被追溯关联,除非它们下次被导入,或者您运行一次回填操作(请参见 回填现有发现项)。

哪些内容会被关联

关联依据四种信号进行分组。其中三种是精确匹配——只有当两个发现项确实指向同一事物时才会创建链接—— 另外一种则是带有标注的启发式匹配。

根本原因类型发现项在以下情况下会被分组……示例匹配方式
组件引用相同版本的相同软件组件log4j-core 2.14.1精确匹配
CVE引用相同的 CVE 标识符CVE-2021-44228精确匹配
资源指向相同的基础设施对象aws_s3_bucket.logs精确匹配
端点在相同 URL 上报告相同的弱点类别CWE-79 at example.com/search启发式匹配

一个发现项会加入所有适用于它的聚类,而不仅仅是一个。例如,一个针对 log4j-core 2.14.1、携带三个 CVE 的 SCA 发现项会加入四个根本原因:其组件聚类,以及每个 CVE 各一个 聚类。正是这一点使得只报告 CVE 的容器镜像发现项,能够与报告该组件的 SCA 发现项建立关联。

组件匹配

在使用 Locations 数据模型的情况下,组件以**软件包 URL(purl)**为键, 并会去除限定符和子路径,因此针对不同发行版或架构报告的同一软件包会形成一个聚类,而不是多个。仅携带旧版 component_name / component_version 字段的发现项,则以这些字段为键。

没有可用组件信息的发现项会被跳过,而不会被分组:否则,缺失的版本号,或者某些 SBOM 格式生成的 unknown-package 占位符,会把所有没有组件信息的记录都合并到一个毫无意义的聚类中。

CVE 匹配

CVE 标识符会被转换为大写并去除多余空格,因此 cve-2021-44228CVE-2021-44228 会落入 同一个聚类。只有 CVE 标识符会被匹配——GHSA、GO、RUSTSEC 等其他公告前缀虽然在 DefectDojo 的其他地方会被 识别为漏洞 ID,但目前尚不能构成根本原因。

资源匹配

云安全态势管理(CSPM)和基础设施即代码(IaC)工具报告的是资源而非 软件包:例如 S3 存储桶、Kubernetes 命名空间、Terraform 资源块。这些发现项带有名称但没有版本号,因此它们 不是软件组件,也不会按软件组件的方式进行匹配。

资源匹配会根据资源标识符对它们进行分组,并统一大小写,以便拼写方式不同的 工具仍能达成一致。这是一种精确匹配,正是它使得关于 aws_s3_bucket.logs 的 IaC 发现项,能够与关于 已部署存储桶的运行时 CSPM 发现项归入同一个根本原因。

只有带限定符的标识符才会被匹配——资源名称需要带有类型或路径分隔符 (./:)。单独的一个词会被忽略,这样一来,仅仅因为扫描器遗漏了组件版本号的发现项,就不会被错误地 归入与其毫无关系的资源聚类中。

端点匹配

两个扫描同一应用程序的 DAST 工具通常都会在同一 URL 上报告相同的弱点。端点匹配会将这些发现项分组: 根本原因是某个位置上的弱点类别,例如 CWE-79 at example.com/search

这是唯一的启发式信号,并且在出现的任何地方都会被标注为启发式。共享的 purl 或 CVE 是一种身份标识;而"相同的 CWE、相同的 URL"则是一种判断,审阅者应当能够对其做出不同的权衡。 聚类详情会为每个成员标注其匹配类型。

CWE 是必需的。仅凭 URL 只是一个位置,而不是原因——如果不论问题是什么, 都将 /search 上的所有发现项分为一组,会产生庞大而毫无意义的聚类。

比较 URL 时会忽略查询字符串、片段和端口,因此 /search?q=a/search?q=b 被视为同一位置,443 端口和 8443 端口上的同一服务也是如此。

这不会将 SAST 与 DAST 关联起来。 静态发现项标识的是源文件,而动态 发现项标识的是 URL;在两者之间建立映射需要一份路由映射表,而 DefectDojo 并不具备这样的映射表。 端点匹配只会将动态发现项彼此关联。

当某个 CVE 已被某个组件覆盖时

一个发现项会同时加入其组件原因每一个 CVE 原因,因此一个针对 log4j-core 2.14.1、携带两个 CVE 的 SCA 发现项会产生三个根本原因。如果不加处理,这三个原因都会 争夺排行榜的靠前位置——但实际上只有一项是需要完成的工作。将 log4j-core 升级到已修复的版本会直接清除这两个 CVE;并不存在单独的"修复 CVE-2021-44228"这项操作。

因此,当一个 CVE 根本原因的每一个活跃成员发现项,同时也是同一个组件或资源原因的 活跃成员时,该 CVE 根本原因会被标记为已覆盖。已覆盖的原因默认会从根本原因 页面中隐藏,从而使列表只保留您实际可以采取行动的内容。

一旦有一个成员不在该组件范围之内,该 CVE 就会重新独立存在。这正是那种只 报告 CVE、不附带任何组件信息的容器镜像发现项的情形:没有任何组件修复能够触及它,因此该 CVE 确实是一项 独立的工作。这正是关联功能存在的意义所在——揭示这种跨领域的情形,而且它绝不会被隐藏。

在表格上方开启显示已覆盖的 CVE即可查看它们。每一项都会标注覆盖它 的原因,因此可以清楚地知道哪次修复能够清除它。已覆盖的原因只是从默认 列表中隐藏——它们仍保留各自的成员、证据和反馈,仍可以从发现项的根本原因面板访问,之前保存的链接也依然 可以打开。

覆盖状态会在每次运行时重新评估,且是双向的:一旦出现一个未被覆盖的 发现项,该 CVE 就会立即停止被视为已覆盖;而一旦该发现项被修复或被分类处理掉,该 CVE 又会重新变为 已覆盖。拒绝某条链接同样会将该成员从计算中剔除,因为您已经表明它不属于该聚类。

组件原因和资源原因永远不会被标记为已覆盖,即使它们的成员与其他原因的成员 存在重叠。因为每一个都有各自需要升级的版本,所以每一个都是真实的工作。

哪些发现项符合条件

只有处于活动状态且可采取行动的发现项才会被关联。当发现项处于非活动、 已缓解、重复、误报、超出范围或风险已接受状态时,会被排除在外。发现项在被分类处理后会退出其所在的聚类, 因此根本原因的计数始终反映的是尚未处理的工作。

阅读根本原因页面

在侧边栏的管理部分打开根本原因页面。该页面会列出您有权访问的 每一个根本原因,并按排名排序,规模最大、风险最高的排在最前面。

表示的内容
根本原因组件及其版本,或 CVE
类型组件、CVE、资源或端点
修复版本能够修复该问题的版本(当聚类的所有成员对同一个版本达成一致时)
CVE该聚类成员中出现的所有 CVE(组件聚类)
活跃发现项该原因对应的尚未处理的发现项数量
产品影响范围——受影响的产品数量
风险汇总风险,由活跃成员的严重程度累加而成
已静音该聚类是否已被静音

已被某个组件或资源原因完全覆盖的 CVE 原因默认会被隐藏,除非开启了 显示已覆盖的 CVE;请参见 当某个 CVE 已被某个组件覆盖时

选择一行即可打开该聚类,列出每个成员发现项及其严重程度、产品、 领域、匹配类型,以及关联它的证据。证据是按链接逐条记录的,因此聚类始终能够解释自身:组件链接 会记录其匹配所依据的 purl,CVE 链接会记录该标识符,端点链接会记录 URL 和 CWE。匹配列对于 组件、CVE 和资源链接显示为 exact(精确),对于端点链接显示为 heuristic(启发式),因此判断类的结果 绝不会被当作身份标识来呈现。

汇总风险是根据活跃成员的严重程度确定性地累加而成(严重 100、高 70、中 40、低 10、信息 1)。它不依赖于优先级排序引擎是否已启用。

修复版本取自各成员自身报告的修复版本,只有当所有报告了修复版本的成员报告的 都是同一个版本时,才会显示。由于不同扫描器的结论可能不一致,而且一个 CVE 聚类可能横跨多个组件, 各自在不同版本才得到修复,因此在没有唯一答案的情况下,该列会留空,而不是随意选取一个。

您所看到的内容取决于您的访问权限

成员、计数和影响范围都会先根据您有权查看的发现项进行过滤,排名是在 过滤之后才计算得出的。因此,拥有不同产品访问权限的两名用户,针对同一个根本原因会看到不同的计数;而 如果某个聚类的成员您完全无权查看,那么该聚类对您来说根本不会出现。

关联还会出现在哪些地方

在发现项上

发现项自身的页面带有一个根本原因面板,列出它所属的每一个聚类,并 区分为易受攻击的组件(或资源)以及它所共享的 CVE。这通常是关联功能发挥最大作用的地方:您正在分类 处理某一个发现项,而它会告诉您该修复是共享的。您已拒绝的链接不会在此处重新出现。

在发现项优先级中

一个横跨多个产品的根本原因,会使其每一个成员发现项都变得更加紧迫,因为一次 修复就能清除所有这些发现项。因此,优先级会随着发现项所属的最广根本原因的影响范围而上升:

  • 仅限于一个产品的聚类不会产生任何加成——不存在"一次修复清除多个"的情形。
  • 每多影响一个产品都会增加一点权重,但存在上限,因此一个范围极广的聚类也无法压过严重程度本身。
  • 计入计算的是最广的那个聚类,而不是所有聚类的总和,因此一个发现项不会仅仅因为携带许多 CVE ID 就被推高 优先级。
  • 您已拒绝的链接不再计入计算。已静音的聚类仍会计入:静音只是将其从排行榜中隐藏,并不代表 这些发现项之间没有关联。

该权重可以按产品在优先级排序引擎中作为关联乘数进行调整, 与严重程度、可利用性、端点和可达性并列。当功能开关关闭时,该项会完全消失,因此在不使用关联功能的 实例上,评分不会发生变化。

在仪表板上

热门根本原因可作为仪表板小组件使用,列出排名最靠前的聚类及其 发现项数量、受影响产品和风险。可从小组件选择器中添加该组件;只有在功能启用时,它才会出现在选择器中。它的 计数方式与页面一样,同样取决于您的访问权限。

对聚类提供反馈

关联是对您数据的一种判断,因此您可以对其进行更正。

  • 确认某个成员,以记录该链接是正确的。
  • 拒绝某个成员,以记录该链接是不正确的,这会将其从聚类的活跃成员列表中移除。
  • 静音整个根本原因,使其不再在排行榜中争夺关注度。取消静音可将其恢复。

反馈是持久保存的。普通的重新导入变动——例如某个发现项被缓解后又重新 被激活——不会抹去确认或拒绝的记录,即使某个已静音的聚类暂时没有任何成员,也不会被清理掉。只有系统 自行创建的链接,才会在不再适用时被自动核销。

关联运行的方式与时机

关联会在每次导入和重新导入之后自动异步运行,作用于本次导入所 影响的发现项。它是尽力而为式的:关联内部发生的失败会被记录并吞掉,绝不会导致触发它的那次导入失败。

由于该过程是幂等的,针对同一批发现项重复运行会收敛到相同的结果, 而不会产生任何重复内容。随着发现项的变化,引擎也会进行调和:组件版本升级会将发现项移动到新的聚类,并 在旧聚类变空后将其清除。

回填现有发现项

要关联在该功能启用之前就已存在的发现项,请运行管理命令。省略 参数可重新计算整个组合,也可以将其限定在单个产品范围内:

python manage.py recompute_correlations
python manage.py recompute_correlations --product-id 42

API 公开的内容

根本原因可通过标准 API 读取,因此您无需通过 UI,就可以将其拉取到报告中、 据此创建工单,或将其作为指标进行跟踪。

  • GET /api/v2/root_causes/ 列出所有根本原因,排序方式与页面上相同。
  • GET /api/v2/root_causes/{id}/ 返回单个根本原因及其成员发现项,每个成员都附带 关联它的证据,以及该匹配是精确匹配还是启发式匹配。

这两个接口都是只读的。确认、拒绝和静音目前仅能通过 UI 完成;在该功能处于 测试版期间,这些操作被有意未予公开,这样一来,日后添加它们时就不会破坏您已经基于现有接口构建的任何内容。

列表接口支持的过滤条件:cause_typeexactin)、mutedidentity_keyexacticontains)以及 display_name__icontains

在针对该接口编写脚本之前,有两点行为值得了解:

  • 计数取决于令牌的访问权限,这与 UI 中的行为完全一致。拥有不同产品访问权限的两个令牌, 针对同一个根本原因会得到不同的 active_member_countproduct_countrisk_score。这是设计使然——这些数字描述的是调用方所能看到的内容——因此不要将它们当作 整个组合的总数来对待。
  • 已覆盖的 CVE 原因不会出现在列表中,但始终可以通过 id 检索到。传入 ?include_subsumed=true 即可将其包含在内;您之前存储的根本原因 id,即使在该原因变为已覆盖之后, 仍可通过 GET /api/v2/root_causes/{id}/ 正常访问。每个已覆盖的原因都带有 subsumed_by_idsubsumed_by_name,以便您了解是哪次修复清除了它。

如果功能开关处于关闭状态,这两个接口都会返回 403,而不是 404——接口本身是 存在的,只是尚未启用。

与全局组件去重的交互

全局组件去重 会将跨产品的 SCA 发现项标记为重复项,而重复项不会被关联。因此,当两个功能同时启用时,根本原因的 成员计数反映的是幸存下来的原始发现项,而不是每一次出现。这两个功能的匹配依据也不同——全局组件去重 依据组件名称和版本进行匹配,而关联功能使用完整的软件包 URL——因此虽然支持同时启用这两个功能,但它们 各自产生的计数并不能直接比较。