# QingLong 3.0 镜像 OS 漏洞门 本流程覆盖 `control|control-ai|admin|local|worker × amd64|arm64` 十个 release candidate 的 OS/base image package,不替代 npm production dependency audit、CycloneDX、BuildKit SBOM/provenance、Cosign 或 GitHub attestation。 ## 日常检查 ```sh pnpm audit:image-os-vulnerability-policy:ql3 pnpm audit:image-release:ql3 pnpm audit:cluster-deployment:ql3 ``` 默认 exception policy 位于 `deploy/containers/ql3-os-vulnerability-exceptions.json`,当前必须返回: ```json {"schemaVersion":1,"fixture":"qinglong/image-os-vulnerability-exceptions@v1","compatible":true,"exceptionCount":0,"imageExceptionCounts":{"admin":0,"control":0,"local":0}} ``` 真实 scan 在 QL3 CI 已有十个 native build 后执行;受保护 release 的十个 native job 各自只构建一次 OCI layout tar, Trivy 直接扫描该 tar。扫描成功后才解包、审计并上传同一 OCI graph;publisher 只合并 amd64/arm64 graph,不再 build。 只有六份 artifact 和 D-236 私密 evidence job 全部成功,publisher 才会启动。scanner/DB 网络故障与漏洞命中使用相同 的失败关闭结果,但日志会区分下载错误和 finding。 ## Build-once 发布链 每个 `control|admin|local × amd64|arm64` native job 固定执行: 1. Buildx 用 exact Dockerfile、source revision、SBOM 与 provenance 生成 `${RUNNER_TEMP}/ql3-native/image.oci.tar`; 2. Trivy 以 `input` 扫描该 tar,禁止 daemon/candidate tag; 3. 扫描成功后解包到 `layout/`,repository recorder 复验 platform、source、SBOM/provenance 和全部 digest,写入 `evidence.json`,随后删除 tar; 4. 以 `run_id/run_attempt/image/arch` 唯一命名上传 `layout + evidence`,保留 1 天且禁止 overwrite。 publisher 的固定顺序是:下载同一 run attempt 的两份 native artifact → 本地重验并确定性 merge → 生成最终 OS vulnerability predicate → checksum 验证 regctl → 登录 GHCR → `IMAGE@DIGEST` import → 读取远端 digest 比对 → Cosign/SLSA/CycloneDX/OS-vulnerability attest 与 verify → manifest/rollout verify → 最后创建 version 与完整 commit tag。 publisher 不得安装 Buildx/QEMU、运行 Dockerfile 或在 tag promotion 后追加 step。 ## 创建短期例外 优先升级 digest-pinned Node base。只有当前没有可部署修复且风险被明确接受时,才能在 central JSON 的 `exceptions` 中增加一项: ```json { "id": "CVE-2026-12345", "images": ["admin", "control"], "purls": ["pkg:deb/debian/libssl3@3.0.0-1"], "owner": "security/platform", "ticket": "QLSEC-123", "expiresOn": "2026-08-15", "rationale": "Temporary exposure accepted while the fixed base image is qualified." } ``` 约束: - entries 按 `id` 严格升序,ID 唯一;images/purls 各自唯一且升序; - 只接受 `CVE-*` 与 `pkg:apk|deb|rpm`,不能屏蔽 npm/library finding; - `expiresOn` 必须晚于 UTC 当天且最多 30 天,到期当天自动失败; - owner、ticket、rationale 缺一不可;ticket 必须先完成独立安全复核; - 不得使用 `ignore-unfixed=true`、裸 `.trivyignore`、skip path、allow-all Rego 或无期限 VEX 代替本策略。 提交前执行: ```sh node --test test/back/ql3ImageOsVulnerabilityPolicy.test.cjs node --test test/back/ql3ClusterImageReleaseAudit.test.cjs pnpm audit:image-os-vulnerability-policy:ql3 pnpm audit:image-release:ql3 ``` 策略生成器会为每个 image 创建临时 `.trivyignore.yaml`;Trivy action 会把该低敏 exception view 显示在 job log。 不得在 rationale、owner 或 ticket 中写 credential、内部 URL、个人数据或 Secret。 ## 升级 scanner 或基础镜像 1. 阅读 Trivy 官方 release 与 security advisory,确认目标版本不在已知暴露窗口; 2. 解析 signed/immutable action release 到完整 commit SHA,审查 composite action 的所有 nested action pin; 3. 固定 scanner exact version,禁止 `latest`;保持 cache false、OS-only、unfixed 不忽略; 4. 对 Node base 的多架构 manifest digest 执行签名/来源核验;四个 production Dockerfile 的 runtime stage 必须同步更新 exact digest; 5. 更新 ADR、静态审计 expectation 与 mutation tests;执行十个 native scan 后才能发布; 6. 修复后删除已不需要的 exceptions,不等待 `expiresOn`。 ## 失败恢复 - native job 失败或 artifact 未生成:重新运行整个 release;不要手工上传 artifact 或把旧 run artifact 改名复用; - artifact/evidence/layout digest 不一致:视为供应链完整性失败,从 native build 重新开始,不通过重新打 tar 修复; - regctl checksum 或下载失败:禁止回退到未固定 binary、`docker push` 或先推 candidate tag; - exact digest 已导入但证明/验证失败:不要创建 tag。保留 digest 用于调查,修复后由新 run 重建; - tag promotion 失败:确认目标 tag 没有指向其他 digest;不得覆盖不一致 tag来“完成”发布; - 清理 runner 时只删除该 job 的 `${RUNNER_TEMP}/ql3-native`、`${RUNNER_TEMP}/native` 和 merge 输出,不能删除 workspace、D-236 owner-private source 或共享 Docker/Kind 资源。 ## 当前证据边界 仓库已证明 policy、workflow、base pin、build-once/scanned-digest merge 和 fail-closed mutation contract。本机尝试 真实 OCI attested build 时,Docker container driver 获取 BuildKit SBOM scanner 遇到网络超时并已中止,临时目录已 删除,因此没有本地 live CVE clean 结论。首次 GitHub-hosted 六矩阵成功记录和首个 evidence-backed GHCR exact-digest publish 仍属于外部 Release Gate。control/admin 的 exact Dockerfile 已在本机 arm64 成功构建并确认 `10001:10001`, 对应临时 image 随后已删除;这只证明 build/base pin,不替代漏洞数据库扫描。