From 300b07e4ebbe2d84c41857c907dcea6f55cbc11e Mon Sep 17 00:00:00 2001 From: randypanding Date: Wed, 19 Aug 2026 03:39:31 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20drift-check=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?=C2=A711=20CI-Workflows=20=E5=A4=A7=E7=89=88=E6=9C=AC=E6=8C=87?= =?UTF-8?q?=E9=92=88=E5=AE=8C=E6=95=B4=E6=80=A7=E6=A3=80=E6=B5=8B=EF=BC=88?= =?UTF-8?q?=E7=BA=A2=E9=98=9F=20#6-A=EF=BC=8CADR-0016=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全部业务仓 gate 引用 CI-Workflows@v1 浮动指针,release-tags ruleset 的 admin bypass 使指针可被强移且此前无任何检测。§11 每日校验不变式 vN==最高 vN.x.y 的 commit(附注 tag 解引用后比对)——强移 24h 内检出。 --- governance/drift-check.sh | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/governance/drift-check.sh b/governance/drift-check.sh index 25cfdc5..d997b46 100755 --- a/governance/drift-check.sh +++ b/governance/drift-check.sh @@ -299,6 +299,52 @@ else [[ $GHOST -eq 0 ]] && ok "adr-reference-existence(窗口内合并 PR 的 ADR 引用全部真实)" fi +# ---------- 11. CI-Workflows 大版本指针完整性(供应链,红队 #6-A / ADR-0016 决策 3)---------- +# 全部业务仓 gate 引用 CI-Workflows@v1 浮动指针;release-tags ruleset 的 admin bypass +# 使指针可被强移(改变所有业务仓实际执行的 CI 内容)。声明不变式(CI-Workflows README +# 版本策略):vN 恒指向最高 vN.x.y 的 commit。本节每日校验——admin 强移指针 24h 内检出, +# 不可见通道变可检测。附注 tag 指向 tag 对象(非 commit),须解引用后比对。 +CW_REFS=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/matching-refs/tags/") +if ! jq -e 'type == "array"' <<<"$CW_REFS" >/dev/null 2>&1; then + drift "CI-Workflows tag refs 拉取失败,大版本指针完整性无法校验(fail-closed): $(jq -r '.message // "非数组"' <<<"$CW_REFS" 2>/dev/null || echo 传输失败)" +else + # 解引用到 commit SHA(轻量 tag 直指 commit 零额外请求;附注 tag 走 tag 对象一跳) + cw_commit_sha() { # $1=tag 名( refs/tags/ 去前缀) + local row obj type sha + row=$(jq -c --arg r "refs/tags/$1" '.[] | select(.ref == $r)' <<<"$CW_REFS") + [[ -z "$row" || "$row" == "null" ]] && { echo ""; return; } + obj=$(jq -c '.object' <<<"$row") + type=$(jq -r '.type' <<<"$obj"); sha=$(jq -r '.sha' <<<"$obj") + if [[ "$type" == "tag" ]]; then + api "https://api.github.com/repos/$ORG/CI-Workflows/git/tags/$sha" | jq -r '.object.sha // empty' + else + echo "$sha" + fi + } + CW_NAMES=$(jq -r '.[].ref | sub("^refs/tags/"; "")' <<<"$CW_REFS") + PTR_FOUND=0 + while IFS= read -r ptr; do + [[ -n "$ptr" ]] || continue + PTR_FOUND=$((PTR_FOUND+1)) + N="${ptr#v}" + HIGHEST=$(grep -E "^v${N}\.[0-9]+\.[0-9]+$" <<<"$CW_NAMES" | sort -V | tail -1) + PTR_SHA=$(cw_commit_sha "$ptr") + if [[ -z "$HIGHEST" ]]; then + drift "CI-Workflows 指针 tag '$ptr' 存在但无任何 v${N}.x.y 具体版本 tag——指针失去锚点(发布流程漏步,README 版本策略)" + elif [[ -z "$PTR_SHA" ]]; then + drift "CI-Workflows 指针 '$ptr' 解引用失败(fail-closed)" + else + HIGH_SHA=$(cw_commit_sha "$HIGHEST") + if [[ "$PTR_SHA" == "$HIGH_SHA" ]]; then + ok "CI-Workflows 指针 '$ptr' == $HIGHEST(${PTR_SHA:0:7})" + else + drift "CI-Workflows 指针 '$ptr'(${PTR_SHA:0:7})≠ 最高版本 $HIGHEST(${HIGH_SHA:0:7})——指针被强移或发布流程漏步(不变式 vN==最高 vN.x.y,红队 #6-A)" + fi + fi + done < <(grep -E '^v[0-9]+$' <<<"$CW_NAMES" | sort -V) + [[ $PTR_FOUND -eq 0 ]] && ok "CI-Workflows 无大版本指针 tag(不变式不适用)" +fi + echo "----------------------------------------" if [[ $DRIFTS -gt 0 ]]; then echo "结果: $DRIFTS 项漂移。修复: bash governance/apply.sh 或手动改回" From 8e858360c9364d5f92fcf19110ffe55cc8e0250b Mon Sep 17 00:00:00 2001 From: randypanding <66171646@users.noreply.github.com> Date: Wed, 19 Aug 2026 04:02:22 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20drift-check=20=C2=A711=20=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E8=81=9A=E5=90=88=20+=20=E5=BF=85=E9=9C=80=E6=8C=87?= =?UTF-8?q?=E9=92=88=E5=AD=98=E5=9C=A8=E6=80=A7=EF=BC=88review=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回应 qodo 意见(2 条 High 落码,1 条 Medium 凭据项以设计依据回复): 1. Tag 分页假绿(High):matching-refs 此前只读第一页,tag 超过单页后 HIGHEST 计算基于残缺集合。改为 per_page=100 逐页聚合,每页验证为 数组,任一页失败 fail-closed 拒用部分结果;>50 页(5000+ tag)视为 异常同样 fail-closed。 2. 缺失 v1 判正常(High):删除 v1 后旧逻辑输出"无大版本指针(不变式 不适用)"=假绿。v1 是全部业务仓 gate 的供应链入口(REPOS.yaml: "业务仓引用 @v1"),指针缺失必须报漂移。新增 CW_REQUIRED_POINTERS 存在性检查;v2+ 出现后仍由既有循环自动纳入锚点/一致性校验。 3. 凭据项(Medium,不落码):§11 与 §1-§10 共用 GOVERNANCE_TOKEN 是 AGENTS.md 声明的 drift-check 接口("GH_TOKEN= bash governance/drift-check.sh");组织级检测本就需要 org 权限,本节 只读公开仓 refs,复用不扩大暴露面;工作流仅 schedule/dispatch 于 可信 main 运行。已在代码注释中固化该威胁模型说明。 验证:对真实 API 全量跑通——"指针 'v1' == v1.2.0(8cccf1b)"+"必需 大版本指针存在(v1)"双双 OK;bash -n 通过。 --- governance/drift-check.sh | 42 +++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/governance/drift-check.sh b/governance/drift-check.sh index d997b46..5002fa8 100755 --- a/governance/drift-check.sh +++ b/governance/drift-check.sh @@ -304,9 +304,29 @@ fi # 使指针可被强移(改变所有业务仓实际执行的 CI 内容)。声明不变式(CI-Workflows README # 版本策略):vN 恒指向最高 vN.x.y 的 commit。本节每日校验——admin 强移指针 24h 内检出, # 不可见通道变可检测。附注 tag 指向 tag 对象(非 commit),须解引用后比对。 -CW_REFS=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/matching-refs/tags/") -if ! jq -e 'type == "array"' <<<"$CW_REFS" >/dev/null 2>&1; then - drift "CI-Workflows tag refs 拉取失败,大版本指针完整性无法校验(fail-closed): $(jq -r '.message // "非数组"' <<<"$CW_REFS" 2>/dev/null || echo 传输失败)" +# 凭据说明(评审项):本节与 §1-§10 共用 GOVERNANCE_TOKEN——AGENTS.md 声明的 +# drift-check 接口即 "GH_TOKEN= bash governance/drift-check.sh"(组织级 +# 检测 rulesets/admin/PR 清单本就需要 org 权限,token 已在 job 内);本节只读公开仓 +# CI-Workflows 的 refs,复用不扩大暴露面。工作流仅 schedule/dispatch 于可信 main +# 运行,脚本不受 PR 控制(与 gate.yml 不向 PR 上下文注入 org secret 同一威胁模型)。 +# 分页聚合(评审项:单页请求在 tag 数超页容量后会拿旧集合比较出假绿): +# 逐页拉取并验证每页均为数组,任一页失败即 fail-closed,不用部分结果继续比较。 +CW_REFS="[]" +CW_PAGE_FAIL=0 +CW_PAGE=1 +while :; do + CW_PAGE=$(api "https://api.github.com/repos/$ORG/CI-Workflows/git/matching-refs/tags/?per_page=100&page=$CW_PAGE") + if ! jq -e 'type == "array"' <<<"$CW_PAGE" >/dev/null 2>&1; then + CW_PAGE_FAIL=1 + break + fi + CW_REFS=$(jq --argjson acc "$CW_REFS" '. as $p | $acc + $p' <<<"$CW_PAGE") + [[ "$(jq 'length' <<<"$CW_PAGE")" -lt 100 ]] && break + CW_PAGE=$((CW_PAGE+1)) + [[ $CW_PAGE -gt 50 ]] && { CW_PAGE_FAIL=1; break; } # >5000 tag 视为异常,fail-closed +done +if [[ $CW_PAGE_FAIL -ne 0 ]]; then + drift "CI-Workflows tag refs 分页拉取失败(第 $CW_PAGE 页非数组/超界),大版本指针完整性无法校验(fail-closed,不用部分结果比较)" else # 解引用到 commit SHA(轻量 tag 直指 commit 零额外请求;附注 tag 走 tag 对象一跳) cw_commit_sha() { # $1=tag 名( refs/tags/ 去前缀) @@ -322,10 +342,8 @@ else fi } CW_NAMES=$(jq -r '.[].ref | sub("^refs/tags/"; "")' <<<"$CW_REFS") - PTR_FOUND=0 while IFS= read -r ptr; do [[ -n "$ptr" ]] || continue - PTR_FOUND=$((PTR_FOUND+1)) N="${ptr#v}" HIGHEST=$(grep -E "^v${N}\.[0-9]+\.[0-9]+$" <<<"$CW_NAMES" | sort -V | tail -1) PTR_SHA=$(cw_commit_sha "$ptr") @@ -342,7 +360,19 @@ else fi fi done < <(grep -E '^v[0-9]+$' <<<"$CW_NAMES" | sort -V) - [[ $PTR_FOUND -eq 0 ]] && ok "CI-Workflows 无大版本指针 tag(不变式不适用)" + # 必需指针存在性(评审项:删除 v1 ≠ "无指针即不适用"——v1 是全部业务仓 gate 的 + # 供应链入口(REPOS.yaml CI-Workflows role:"业务仓引用 @v1"),指针被删= + # admin bypass 绕过发布流程,必须报漂移。v2+ 指针出现后由上方循环自动纳入 + # 锚点/一致性校验;必需集合当前只声明 v1——v2 落地时随 ADR 扩充此常量。) + CW_REQUIRED_POINTERS="v1" + REQ_MISSING=0 + for req in $CW_REQUIRED_POINTERS; do + if ! grep -qx "$req" <<<"$CW_NAMES"; then + drift "CI-Workflows 必需大版本指针 '$req' 缺失(业务仓 @v1 供应链入口被删——红队 #6-A:release-tags ruleset admin bypass 通道)" + REQ_MISSING=1 + fi + done + [[ $REQ_MISSING -eq 0 ]] && ok "CI-Workflows 必需大版本指针存在($CW_REQUIRED_POINTERS)" fi echo "----------------------------------------"