mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
101 lines
8.2 KiB
Markdown
101 lines
8.2 KiB
Markdown
# ADR-0062:Profile 漏洞归属与 Legacy 依赖树治理
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-20
|
||
- 关联 RFC:QL-RFC-0001 D-05、D-35、D-37、D-40、D-61、D-65、D-67
|
||
- 关联 ADR:ADR-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 三组收敛为显式 subpath,ADR-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 low;2026-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 通过。
|