fix(ci): ID:15 排除表守卫改为「注入合成行真跑」,杜绝形似而无效的假绿

原判据(efe0cf1)剥注释后查 token:`${reason// /}` + `exit 1` 都在、提示文案都在,
但把 if 条件改成 `&& false` 后条件恒假、步骤实跑 EXIT=0,判据仍然绿 —— 假绿。

改为从 race job 里抽出「计算 -race 排除表并打印」步骤的 run 体,在临时 bash 里真跑三次:
  1. 原样                 → 必须 EXIT=0(正向基线)
  2. 注入 reason 空的合成行 → 必须 EXIT=1
  3. 注入 ticket 空的合成行 → 必须 EXIT=1
两条守卫各用只违反自己的输入单独打,不用一条顶另一条的包。
用合成行而非真实 webui 排除项 ⇒ 该排除项修好后被删掉也不会误报。

验证矩阵(verify_v4.py,5 变异 + 1 误报 + 基线):
  ✓ reason 守卫失效(token 全在 + && false)  EXIT=1
  ✓ ticket 守卫失效(token 全在 + && false)  EXIT=1
  ✓ 删 cmd/gui 剔除                          EXIT=1
  ✓ 启动图判据步去掉 -count=1                 EXIT=1
  ✓ race 退回写死两包                        EXIT=1
  ✓ 删 webui 排除行(误报检查)               EXIT=0
  ✓ 还原后基线                               EXIT=0
go build ./... = 0 error,go vet ./... = 0 error
This commit is contained in:
JianFeeeee
2026-10-24 12:41:51 +08:00
parent efe0cf119b
commit 653c87f5f2

View File

@ -96,18 +96,67 @@ for _n, r in evid:
check("DATA RACE" in r,
"取证步骤应显式检查 DATA RACE 报告数(否则取证没意义)")
# ── 3. skip 排除表:每项必须写原因,无原因必须步骤自己失败 ──
# ── 3. skip 排除表:真跑一次坏输入,要求它必须失败 ──
#
# ★ 这里曾经写成 `any("没有写原因" in r ...)` —— 只检查**提示文案还在**。
# 实测:把 `if [ -z "${reason// /}" ]; then` 改成 `if false; then`
# (文案全保留、校验实际失效),该断言仍然绿,而该步骤实跑 EXIT=0
# —— 也就是说「无原因必须失败」这条保证彻底破掉,判据却看不见。
# 这正是本脚本自己在 docstring 里批判的「被文案顶包」,不能自己再犯。
# ⇒ 改为剥注释后断言**可执行的判空逻辑**:必须同时出现判空变量与 exit 1。
race_code = "\n".join(code_lines(r) for _n, r in race)
check('${reason// /}' in race_code and "exit 1" in race_code,
"race job 排除表缺少真校验:剥注释后必须能看到 ${reason// /} 判空 + exit 1,"
"只有提示文案不算(把 if 条件改 false 就能骗过纯文案断言)")
# ★ 这里换过三版,每一版的失效方式都值得记:
# v1 `any("没有写原因" in r ...)` —— 只查提示文案;把 if 条件改 false 仍绿
# v2 剥注释后查 `${reason// /}` + `exit 1` —— 两个 token 都在仍可能不成立;
# 实测 `if [ -z "${reason// /}" ] && false; then` 依然绿
# v3 真跑、但拿真实 webui 那一行做坏输入 —— 该排除项修好被删掉时**误报红**
# v4(本条)用**合成行**注入 TABLE 再真跑:
# · 不依赖任何真实排除项 ⇒ 排除项增删不会引起误报
# · 真跑 ⇒ `&& false` 这类「形似而无效」的改动会被抓出来
#
# 两条守卫必须**各自**用只违反它的输入来验,否则一条会顶另一条的包
# (第一版我把 reason 与 ticket 同时清空,ticket 守卫兜住了,reason 守卫死没死看不出来)。
import subprocess as _sp
import tempfile as _tf
_tbl = None
for _n, _r in race:
if "计算 -race 排除表并打印" in (_n or ""):
_tbl = _r
if _tbl is None:
fails.append("race job 找不到「计算 -race 排除表并打印」步骤 —— 无法自测排除表守卫")
elif "${{" in _tbl:
fails.append("排除表步骤含 GitHub 表达式,本脚本无法本地执行它(需改用别的验证方式)")
else:
import re as _re
_m = _re.search(r"read -r -d '' TABLE <<'EOF'[^\n]*\n(.*?)\nEOF\n", _tbl, _re.S)
if _m is None:
fails.append("排除表步骤里找不到 TABLE heredoc —— 无法注入合成坏行")
else:
def _run_body(body):
fd, tmp_sh = _tf.mkstemp(suffix=".sh")
with __import__("os").fdopen(fd, "w", encoding="utf-8") as f:
f.write(body)
out = _tf.mktemp()
env = dict(__import__("os").environ)
env["GITHUB_OUTPUT"] = out
try:
return _sp.run(["bash", tmp_sh], capture_output=True, text=True, env=env)
finally:
__import__("os").unlink(tmp_sh)
if __import__("os").path.exists(out):
__import__("os").unlink(out)
def _inject(line):
inner = _m.group(1)
new_inner = (line + "\n" + inner) if inner.strip() else line
return _tbl[:_m.start(1)] + new_inner + _tbl[_m.end(1):]
# 正向:原样必须通过
if _run_body(_tbl).returncode != 0:
fails.append("排除表步骤用当前 ci.yml 实跑不通过(正向基线坏)")
_probe = "test|internal/probe/synthetic|TestSyntheticProbe"
# 只打 reason:reason 空、ticket 有效
if _run_body(_inject(_probe + "||PROBE-TICKET")).returncode == 0:
fails.append("合成行「reason 空 / ticket 有效」竟通过 ⇒ reason 守卫已失效"
"(形似而无效的改动,例如 if 条件加 && false)")
# 只打 ticket:reason 有效、ticket 空
if _run_body(_inject(_probe + "|合成探针原因|")).returncode == 0:
fails.append("合成行「reason 有效 / ticket 空」竟通过 ⇒ ticket 守卫已失效")
# ── 4. go job:整机启动图判据必须单列一步跑 kernelboot/modules ──
gojob = step_runs("go")