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

101 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 通过。