# 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 通过。