Files
qinglong/docs/adr/ADR-0062-profile-vulnerability-ownership-and-legacy-dependency-remediation.md

8.2 KiB
Raw Permalink Blame History

ADR-0062Profile 漏洞归属与 Legacy 依赖树治理

  • 状态:Proposed
  • 日期:2026-07-20
  • 关联 RFCQL-RFC-0001 D-05、D-35、D-37、D-40、D-61、D-65、D-67
  • 关联 ADRADR-0038、ADR-0040、ADR-0042、ADR-0044、ADR-0061、ADR-0065、ADR-0066、ADR-0068、ADR-0069、ADR-0070、ADR-0071、ADR-0072、ADR-0073、ADR-0074

上下文

QingLong 同一仓库同时包含 2.x legacy 根应用和 3.0 的 runtime-core、cluster、admin、Worker 等独立 workspace。根目录执行一次 production audit 得到的是开发 workspace 的并集,不是 edge、standalone、cluster-control 或 Worker 用户实际安装的产物。若只看总数,路由设备会为不会安装的 PostgreSQL/PKI 依赖承担错误结论;若完全忽略根结果,又会让仍服务 2.x 用户的高危依赖失去责任人。

首次基线中,五个 3.0 孵化 importer 均没有 advisory,但 legacy 根存在 2 critical、25 high、35 moderate、5 low。后续切片逐步增加 Profile、local/cluster storage、execution、Secret、Identity、Owner ceremony 与 CLI 边界;ADR-0087 把 package-per-use-case 过度拆分的 Owner maintenance、execution 和 Owner ceremony 三组收敛为显式 subpathADR-0106 又把四个无独立依赖责任的 Profile wrapper 迁入现有组合包 subpath。再次按单 consumer 规则收敛 ceremony→console 与 GC CLI→maintenance 后,当前受审集合为二十一个 importer。需要同时解决“按产物准确裁决”和“旧架构债务继续治理”,不能用其中一个掩盖另一个。

决策

1. Profile importer 是 3.0 漏洞裁决单位

当前孵化期漏洞报告必须按以下已登记 3.0 package importer 的 dependency path 归属:

  • packages/ql3-runtime-core
  • packages/ql3-local-command-file
  • packages/ql3-local-sqlite
  • packages/ql3-local-secret
  • packages/ql3-local-secret-admin
  • packages/ql3-local-identity
  • packages/ql3-local-owner-keyring
  • packages/ql3-local-owner-console
  • packages/ql3-local-owner-cli
  • packages/ql3-local-owner-maintenance
  • packages/ql3-local-profile
  • packages/ql3-local-admin
  • packages/ql3-local-adopted-profile
  • packages/ql3-local-application
  • packages/ql3-local-execution
  • packages/ql3-local-process
  • packages/ql3-local-cutover
  • packages/ql3-cluster-postgres
  • packages/ql3-cluster-control
  • packages/ql3-cluster-admin
  • packages/ql3-worker-runtime

所有已登记 importer 的 production graph 对 high/critical 零容忍。任一命中都会阻止 CI;未知 importer 上的 high/critical 也必须失败并要求先评审、登记。dependency audit 同时枚举 packages/ql3-*,任何未登记 package 即使当前没有 advisory 也会阻断,防止“干净但未受审”的 package 绕过 allowlist。legacy 根 . 单独汇总,不得计入 3.0 package 的兼容结论,也不得从输出隐藏。

runtime-core、SQLite/PostgreSQL adapter、共享本机组合根和 admin authority 是构件,不是面向用户的 Deployment Profile。ADR-0106 后 edge/standalone 由 local-profile/local-adopted-profile 的精确 subpath 作为制品入口,并继续具有 production tarball closure/体积/RSS 门禁;它们当前仍不是最终镜像。最终 release 必须从 Profile 专属构建清单生成产物,并对实际镜像/压缩包产生 SBOM、签名和来源证明;不能因为 workspace audit 或 tarball smoke 通过就宣称发布物安全。

2. 审计输入有界并 fail closed

CI 使用独立 supply-chain job,避免把网络型审计偶然绑定到某个 Worker CPU 矩阵。分类器只读取 pnpm audit --prod --json,并限制总输入、advisory 数、每项 finding/path 数量和文本长度。

JSON 无法解析、缺少 advisories、字段畸形、severity 未知或任一预算超限都返回失败。输出只包含 importer、advisory identity、module、severity 和 version,不复制远端 description、URL 或完整 dependency path,避免日志体积和未受信文本扩散。

3. Legacy 高危债务立即收敛,但保持单独可见

本切片升级有兼容补丁/次版本路径的直接依赖,包括 gRPC、Express、HTTP proxy、JWT、Lodash、Multer、Nodemailer、Sequelize 和 Undici。需要修复 transitive 风险时只允许 parent>child 形式的 override,并 exact pin 已审查版本;禁止用全局 child override 改写整个 workspace。

tar 的跨 major 修复仅限定在 @whyour/sqlite3@mapbox/node-pre-gyp,并额外执行 native rebuild、SQLite open/query 与 resolved-version smoke。其他 override 同样限定到对应父依赖。冻结 lockfile 安装、后端 TypeScript build 与行为测试必须全部通过。

前一轮治理曾把 legacy 根收敛到 0 high、0 critical、9 moderate、2 low2026-07-21 重新查询 advisory 数据后,legacy 根回升为 2 high、9 moderate、2 low、0 critical。它们继续显示在报告中并进入后续 triage,而不是被标记为“无漏洞”。这两个 high 不改变当时隔离 QL3 importer 的兼容结论,但应作为 2.x 发布阻断回归单独修复,不能用 3.0 分账隐藏。现行 registry 为 21 个 importer,发布前必须重新获取联网 advisory 结果。

4. Profile 注册与门禁演进

新增可发布 Profile/package 时必须同时更新:workspace 定义、Profile importer allowlist、dependency/import isolation audit、CI 矩阵和产物构建。只增加 package 而不登记安全门禁不构成可发布 Profile。

当前阈值先固定 high/critical,以避免尚未分类的 legacy moderate 阻塞 3.0 孵化。进入稳定发布前应基于可利用性、可达性、修复可用性和部署暴露面为 moderate/low 建立时限策略;阈值放宽或例外必须新增带到期时间和责任人的 ADR/记录,不能修改脚本静默跳过。

被否决的替代方案

  1. 用 monorepo 根总数阻断所有 Profile:无法表达用户实际安装面,并让 legacy 债务长期阻塞独立 3.0 内核。
  2. 只审计 3.0,隐藏 legacy 根:牺牲仍运行 2.x 的用户,并把迁移期双轨变成无人负责的安全债务。
  3. 对 vulnerable child 使用全局 override:可能同时改写多个不兼容父依赖,难以证明行为边界。
  4. 看到 audit 为零就视为供应链安全:audit 不证明依赖可达性、恶意包、构建来源、签名、许可证或运行镜像内容。
  5. 在每个架构矩阵重复在线 audit:增加 registry 抖动和不一致结论,不提供新的架构兼容证据。

影响与未完成项

正向影响:

  • edge/standalone 不再为 cluster、Worker 或 legacy 未安装依赖承担错误风险结论;
  • cluster/Worker 每个 importer 都有独立、可阻断的 production graph 门禁;
  • legacy 高危债务被实际修复而非用 Profile 分类隐藏;
  • override 的影响范围和回归责任可审查;
  • 在线审计、依赖边界和多架构运行测试各自拥有单一职责。

仍未完成:

  • Profile 专属安装/镜像的 SBOM、签名、SLSA provenance 与发布验签;
  • moderate/low 的稳定发布 SLA、例外登记和自动到期;
  • advisory reachability、恶意包/typosquat、许可证与维护活跃度策略;
  • edge/cluster/Worker 最终产物的解包后 dependency diff
  • registry 不可用时的可信 advisory snapshot、缓存和重放策略。

验证

  1. legacy 根的 critical advisory 不会误阻断干净的 3.0 Profile,但仍出现在 legacyRoot 汇总。
  2. 任一已登记 Profile 出现 high/critical 时报告不兼容并退出非零。
  3. 未审查 importer 的 high/critical、畸形 JSON 或缺少字段均 fail closed。
  4. 当前二十一个已登记 3.0 package importer 都会进入同一 fail-closed 分类器,新增未登记 packages/ql3-* 会被 dependency audit 拒绝;edge/standalone、adopted storage 与 local-application 变体已通过实际 tarball closure 门禁。联网 advisory 数字必须以发布流水线当次结果为准。
  5. 当前 legacy 根为 2 high、9 moderate、2 low、0 critical;该回归单独阻断 2.x 发布,不污染 QL3 importer 结论。
  6. pnpm install --frozen-lockfile --ignore-scripts、后端构建和回归测试通过。
  7. sqlite3 native rebuild、内存数据库查询和 scoped tar@7.5.20 resolution 通过。