TT Lab
开始
学习 学习路径 课程

构建是绿灯,可那个库是谁加进来的

对准边界,只留下今天能修的

在 TT Lab 中继续学习

目标

把 SBOM 与漏洞数据比对,只挑出真正受影响的内容,并按“有没有可修复版本”和 “是否会触及用户”这两条轴区分,用程序写出策略关卡。

为什么重要

第一次运行扫描器会出现几百行红色条目。把这份清单原样发给开发团队, 什么也不会发生——因为所有人都知道不可能全部修复。 能减少的地方有两处。第一,区间判定必须准确。 明明用的是已修复的版本却被扫出来, 只要混进这样的误报,整份清单的可信度就会崩塌。第二, 有可修复版本的和没有的,是两回事。 有可修复版本,今天升级即可; 没有,就要写下缓解措施和期限,作为豁免来管理。如果把这两者混在一起,会议 就只会围绕“严重程度高/低”打转,什么也不会改变。

步骤

  1. 读取 /opt/fixtures/sbom/feeds/advisories.json,把数据的结构写入 /root/adv/01-feed.json。
  2. 把 /opt/fixtures/sbom/sbom/paygate-1.4.2.cdx.json 的 components 展平,写入 /root/adv/02-components.json。
  3. 在 /root/adv/semver_cmp.py 中编写版本比较器。
  4. 精确到边界值,把受影响的内容写入 /root/adv/04-findings.json。
  5. 只把有可修复版本的内容移到 /root/adv/05-upgrade-plan.json。
  6. 把会触及用户的内容和在构建阶段就结束的内容,区分写入 /root/adv/06-shipped.json。
  7. 用 /root/adv/gate-vuln.py 写出策略,并把结果留在 /root/adv/07-verdict.json。
  8. 应用 /root/adv/waivers.json 中的豁免,重新判定并写入 /root/adv/08-verdict.json。

参考

先读懂数据在说什么

读取 /opt/fixtures/sbom/feeds/advisories.json,把 advisories、with_fixed、without_fixed、packages 保存到 /root/adv/01-feed.json。with_fixed 是 ranges 的 events 中至少有一个 fixed 的公告数量,without_fixed 是其余的数量,packages 是按字典序排列的 affected 软件包名称数组。

OSV 的 events 是按时间顺序记录“被引入 / 被修复 / 到这里为止都受影响”的时间线。存在没有 fixed 的公告,这件事本身就是本实验的主题。

把要比对的对象整理成清单

从 /opt/fixtures/sbom/sbom/paygate-1.4.2.cdx.json 的 components 中只保留 name、version、scope,以按名称字典序排列的数组保存到 /root/adv/02-components.json。scope 原样照搬 SBOM 中记录的值。

比对的左边是这份清单,右边是漏洞数据。先把左边展平,后面的步骤就都变得容易了。

用代码固定为什么不能把版本当作字符串比较

在 /root/adv/semver_cmp.py 中编写 compare(a, b) 函数。两个参数都是 X.Y.Z 形式的字符串,a 较小时原样返回 -1,相等时返回 0,较大时返回 1。必须把三段分别作为整数来比较。

如果按字符串比较,会得出 "1.9.0" 比 "1.10.0" 更大。评分器会代入包含这种组合在内的多种情况来测试。

精确匹配到边界值

用 /root/adv/semver_cmp.py 的比较,把 /opt/fixtures/sbom/sbom/paygate-1.4.2.cdx.json 与 /opt/fixtures/sbom/feeds/advisories.json 比对,只把真正受影响的内容以按 id 字典序排列的数组保存到 /root/adv/04-findings.json。每个条目包含 id、package、version、severity、scope、fix_available、fixed_in。severity 取 severity[0].score,scope 照搬 SBOM 中的值。fixed_in 是关闭该版本所在区间的 fixed 值,没有则为 null,fix_available 是它对应的布尔值。introduced 的 "0" 是表示早于任何版本的特殊值。

区间是大于等于 introduced 且小于 fixed——与 fixed 相同的版本已经修复。last_affected 是截至当天已确认的上限,所以这个值本身仍然受影响。一条公告也可能有多个区间。

只把今天就能修复的内容转入计划

从 /root/adv/04-findings.json 中只挑出 fix_available 为 true 的条目,以按 package 字典序排列的数组保存到 /root/adv/05-upgrade-plan.json。每个条目包含 package、from、to、advisory,to 是 fixed_in,advisory 是公告 id。

“有没有可修复的版本”是与严重程度不同的另一条轴。先把今天能升级的和今天升级不了的分开,会议才能结束。

区分会触及用户的和在构建阶段就结束的

把 shipped 和 dev_only 保存到 /root/adv/06-shipped.json。两者都是按字典序排列的公告 id 数组,scope 为 excluded 的发现项归入 dev_only。

开发专用依赖的缺陷不会被打进发布制品。虽然不能因此就置之不理,但如果把它和会触及用户的放在同一类,真正紧急的反而会被淹没。

用程序而不是口头语言来写策略

创建 /root/adv/gate-vuln.py。以 python3 /root/adv/gate-vuln.py <SBOM> <자료>(占位符依次为 SBOM 文件、漏洞数据文件)调用时,向标准输出打印一段包含 blocking(被拦截的公告 id 的字典序数组)和 verdict("pass" 或 "fail")的 JSON;有要拦截的内容时以 1 结束,没有则以 0 结束。策略是“会被打进发布物(scope 为 required)且严重程度为 HIGH 或 CRITICAL 的发现项”。然后把 python3 /root/adv/gate-vuln.py /opt/fixtures/sbom/sbom/paygate-1.4.2.cdx.json /opt/fixtures/sbom/feeds/advisories.json 的输出保存到 /root/adv/07-verdict.json。

如果策略只存在于文档里,每个人读出来的都不一样。评分器也会用其他输入调用这个脚本——在没有可拦截内容的清单上,必须以 0 结束。

豁免只给予无法修复的内容

在 /root/adv/waivers.json 中写入三条豁免。每个条目包含 id、owner、expires、reason,三条分别是 LABHUB-2026-0002(expires 2026-12-31)、LABHUB-2026-0001(expires 2026-12-31)、LABHUB-2026-0007(expires 2026-08-31),owner 全部是 payments-platform。然后以基准日 2026-09-11 应用豁免,把 as_of、waived、rejected、blocking、verdict 保存到 /root/adv/08-verdict.json。豁免只适用于在基准日尚未过期、且 fix_available 为 false 的发现项,被拒绝的条目按 id 字典序记录 id 和 reason("expired" 或 "fix_available")。

豁免是用来记录“目前还修不了”的地方,而不是用来写“不想修”的地方。如果明明有可修复版本,豁免却照样生效,那么这道关卡从那天起就成了摆设。