★★ pi c6f619b8 的 head -132 单点变异成立(一行打穿自检+下界守卫)⇒ 补**逐文件探针**;★ 且我实测出 pi 的建议**本身有洞**(删中间行)⇒ 再加**行号算术不变量**;★★ 并**撤销我上一轮"闭不了"的边界申报**(我当时把"换面"误判成"后退")

★ pi 的变异我复现: `strip_text` 尾接 `| head -132`(**一行**)⇒ rc=**0**、打"裸赋值 0 处"(树上注入 1 处)
   根因三层: ①自检样本只有 **2 行** ⇒ 长度相关变异对它**恒等** ⇒ 自检通过
             ②下界守卫只数"几个文件 source 了" ⇒ source 在 131、违规在 135 ⇒ 保住 source ⇒ 通过
             ③正式扫描看不到违规 ⇒ `_had=0 ∧ _cnt=0` ⇒ "一致" ⇒ fail-closed 不触发
   窗口精确: 逐个 head -N 实测 **打穿区间 = [131,134],宽 4**(source 行, 动作行)
   ⇒ 记法(收 pi 的): **"共用同一实现"只在"样本能触发该变异"时才看得见** ——
     输入相关变异(head/truncate/长度/locale)对短样本恒等 ⇒ 共用实现反而保证"一起静默"

★ 补法一: **逐文件探针**(拿"真文件 + 首尾已知注入"当样本 ⇒ 样本形状 = 被检对象形状)
★★ 但 pi 的建议只放**一个**探针 ⇒ **我只能抓到反方向的截断**(我逐条实测):
     单探针在**尾部**: `head -132` 砍掉它 ⇒ 抓到;`tail -132` 保住它 ⇒ **漏**
     ⇒ 所以**首尾各一**,且要求 head 行号 < tail 行号(顺带抓 `tac`)

★★★ 补法二(**pi 的建议里没有,是我实测出来的洞**): 首尾两探针**仍然不够** ——
   把**中间某一行**(=那条违规行)删掉、首尾都保住 ⇒ 探针全在、顺序也对 ⇒ **假绿**
   实测: `awk 'NR!=135'` / `sed '135d'` ⇒ rc=**0**
   ⇒ 再加**行号算术不变量**: 逐行原位 ⇒ `tail 行号 == head 行号 + 内容行数 + 1`
     中间少一行 ⇒ tail **上移** ⇒ 等式不成立 ⇒ 抓到
   ⇒ 三条件(两端都在 / 顺序对 / 算术成立)合起来 = "**这个过滤器是逐行原位的**"

★★ 撤销上一轮的边界申报(`2e8d5aa` 那个文件的末尾):
   我当时写"§五 闭不了 —— 要闭需一条**独立于本文件**的检查 ⇒ 无限后退 ⇒ 记为边界"。
   ⇒ ★ 错在: 我把"独立"理解成"**另一个文件**",而真正需要的是"**另一次观察**"。
     逐文件探针仍在**本文件**里,却**不经过自检那条判断**(直接看 `_scan_stripped` 的行号算术)。
     ⇒ 实测: 同一 ①∧② 合取,现在 rc=**1**(报"逐文件探针失败")。
   ⇒ 记法: **"无限后退"论证要先问"上一层与这一层是不是同一次观察"** ——
     换了**观测对象**(已知样本 → 被检文件本身)不是后退,是**换面**。
     我把"换面"误判成"后退",于是把一个**可闭**的点申报成了边界。
   ⇒ 该段改为"已闭 + 保留历史(因为错法有教学价值)"
   ⇒ 新增 ⑧ 申报**新探针的射程**: 它证"读入逐行原位",**不**证"注释剥离的语义正确"
     (探针只查行号与两端,不查"哪一列被删")

★ 变异矩阵(11 向,全部真跑):
   [0] 注入违规无过滤器 ⇒1  [1] cat ⇒1  [2] ★pi head -132 ⇒1  [3] tail -132 ⇒1
   [4] tac ⇒1  [5] ★★删中间 135 行 ⇒1  [6] ★★sed 135d ⇒1  [7] 删中间 400 行 ⇒1
   [8] grep -v '^$' ⇒1  [9] sort -u ⇒1  [10] head -c 5000 ⇒1
   非恒红: 干净副本(三调用者全合规)⇒ rc=**0**、报"3 个调用者,裸赋值 0 处" ✓
   criteria-hygiene **9/9** 绿
★ 并发: 全程未碰别人的在飞改动(`install.sh` / `criteria-hygiene.test.mjs` 的临时变异
   都只在我 `/tmp` 的副本上模拟)。
This commit is contained in:
2026-09-25 07:51:36 +08:00
parent 92a51c7edf
commit df836e4ef7

View File

@ -213,7 +213,61 @@ agentmail_require git go' || true)"
unset _nc
# 正式扫描:**先 strip 一次**(唯一实现 `strip_text`),两条通道都用这同一份结果。
#
# ★★ 逐文件探针(2026-09-25 pi 的 `head -132` 单点变异逼出来的,**我实测后加到两个探针**):
# pi 的变异: `strip_text` 尾接 `| head -132` —— **一行改动**同时打穿自检与下界守卫:
# ① 自检样本只有 **2 行** ⇒ `head -132` 对它**恒等** ⇒ 自检照样匹配 ⇒ 通过
# ② 下界守卫看的是"有几个文件 source 了"⇒ install.sh 的 source 在 **131** 行、
# 违规动作在 **135** 行 ⇒ `head -132` 保住 source、砍掉违规 ⇒ 仍是 3 个调用者 ⇒ 通过
# ③ 而正式扫描看不到违规行 ⇒ `_had=0 ∧ _cnt=0` ⇒ "一致" ⇒ fail-closed 不触发
# ⇒ 实测 rc=**0**,打"裸赋值 0 处"(树上注入着 1 处)⇒ **静默漏**
# ⇒ 打穿窗口 = **[source 行, 动作行) = [131,134],宽 4**(我逐个 head -N 量过)
# ★ 根因(pi 的记法,我收): **"共用同一实现"只在"样本能触发该变异"时才看得见** ——
# 一个**输入相关**的变异(head/truncate/长度阈值/locale)对**短样本**是恒等的,
# 于是"共用实现"反而保证了**两者一起静默**。
# ★ 补法: **对每个真文件也跑一次探针**(拿"真文件 + 已知注入"当样本),
# 这样"样本的形状"就是**被检对象本身的形状**,长度相关变异无法对它恒等。
# ⚠️ **我为什么用两个探针而不是一个**(我实测出来的,比 pi 的建议多一步):
# 只放**一个**探针时,它只能抓**从反方向**来的截断 ——
# 探针在**尾部**: `head -132` 砍掉它 ⇒ 能抓;`tail -132` **保住**它 ⇒ 抓不住
# 探针在**首部**: `tail -132` 砍掉它 ⇒ 能抓;`head -132` **保住**它 ⇒ 抓不住
# (我逐条实测:单探针在 head/tail 上各漏一半)
# ⇒ 所以**首尾各一个**,并**要求头探针的行号 < 尾探针的行号**:
# 截断必砍掉其中一端;`tac` 之类保内容但**反转顺序**的变换则由"顺序"这条抓。
# ⚠️ 申报射程: `sort -u` / `grep -v '^$'` 抓不到(它们保留违规行、不改变"有/无");
# 但实测它们对本文件**不构成假绿**(只可能改变**计数**,而计数类失效由 fail-closed 兜)。
_stripped="$(strip_text "$(cat "$f")")"
# ⚠️⚠️ **只靠首尾两个探针还有一个洞,是我实测出来的(pi 的建议里没有这一步)**:
# 把**中间某一行**(例如那条违规行)删掉、首尾都保住 ⇒ 两个探针**都在**、顺序也对
# ⇒ 探针通过,而违规行没了 ⇒ **假绿**。实测: `awk 'NR!=135'` / `sed '135d'` ⇒ rc=**0**。
# ⇒ 补一条**行号算术不变量**: 读入是逐行原位 ⇒ 尾探针行号必须**恰好等于**
# 头探针行号 + 内容行数 + 1(+1 是尾探针自己那一行)。
# 中间少一行 ⇒ 尾探针**上移** ⇒ 等式不成立 ⇒ 抓住。
# 实测(`probe` 原型): 干净 `h=1 t=802 n=800`(802 = 1+800+1 ✓);
# `sed 135d` ⇒ `t=801` ≠ 802 ⇒ 抓到;`head/tail -N` ⇒ 缺探针 ⇒ 抓到;
# `tac` ⇒ `h=802 t=1`(顺序反)⇒ 抓到;`grep -v '^$'` / `sort -u` ⇒ `t` 偏离 ⇒ 抓到。
# ★ 三个条件合起来 = "**这个过滤器是逐行原位的**"(保行数、保顺序、保两端)。
_probe_in="AGENTMAIL_REQUIRE=\"__AM_P_HEAD__\"
$(cat "$f")
AGENTMAIL_REQUIRE=\"__AM_P_TAIL__\""
# 内容行数与探针**同一条读取路径**(都经 `printf '%s\n'` 补回被命令替换吃掉的尾换行)
_probe_n="$(printf '%s\n' "$(cat "$f")" | awk 'END{print NR}')"
_probe_out="$(_scan_stripped "$(strip_text "$_probe_in")" || true)"
_ph="$(grep -F '__AM_P_HEAD__' <<< "$_probe_out" | head -1 | cut -d: -f1)"
_pt="$(grep -F '__AM_P_TAIL__' <<< "$_probe_out" | head -1 | cut -d: -f1)"
_pexp=$(( ${_ph:-0} + _probe_n + 1 ))
if [ -z "$_ph" ] || [ -z "$_pt" ] || [ "$_ph" -ge "$_pt" ] || [ "$_pt" -ne "$_pexp" ]; then
printf ' [FAIL] %s 逐文件探针失败:读入这一环**不是逐行原位**的 —— 它在丢弃或重排内容。\n' "$f" >&2
printf ' head 探针=%s,tail 探针=%s,内容行数=%s(要求 tail == head + 行数 + 1 = %s)。\n' \
"${_ph:-空}" "${_pt:-空}" "$_probe_n" "$_pexp" >&2
printf ' ⇒ 三个条件缺一不可: 两端都在(抓 head/tail 截断)、顺序对(抓 tac)、\n' >&2
printf ' 行号算术成立(抓"只删中间某一行"—— 这一条只靠首尾探针**抓不到**)。\n' >&2
printf ' ⇒ 这一环静音时,**自检看不出来**(自检样本只有 2 行,长度相关变异对它恒等),\n' >&2
printf ' 下界守卫也看不出来(它只数"有几个文件 source 了",不看行号)。\n' >&2
printf ' ⇒ 按红计(绝不能因为两条通道都不说话就当成"没问题")。\n' >&2
exit 1
fi
unset _probe_in _probe_out _probe_n _ph _pt _pexp
_had=0
grep -qE "$AM_SCAN_RE" <<< "$_stripped" && _had=1
_cnt=0
@ -254,14 +308,21 @@ exit 0
# ══════════════════════════════════════════════════════════════════════════════
# ①②③ 是"判据读输入之后"的失效;④ 是"哪些对象进入判据";⑤ 是"谁来验验证器"。
#
# ⑤ ★ 正向对照**保护不了它自己**(pi 2026-09-25 实测,我复现):
# 同时做两件事:(a)`strip_text` 尾接吞掉违规行 (b)自检的**失败分支改成不可达**
# ⇒ rc=**0**,打印"通过 …裸赋值 0 处"(而树上注入着 1 处)⇒ **静默漏**。
# 我实测确认**两个各自动手时不漏**(①单独 rc=1、②单独 rc=1),**合取才漏** ——
# 即这是"共模"的**第二层**: 自检本身也是一条判据,也能被同一类手法打穿。
# ⚠️ 我**不能**闭它: 要闭需一条**独立于本文件**的检查(例如 install.sh 里旁路断言),
# 而那只是把问题推给"那条断言自己会不会被静默"(无限后退)。
# ⇒ 记为**边界**,与下面的"间接赋值"同级。
# ⑤ ~~正向对照**保护不了它自己**~~ ⇒ ★ **已闭(2026-09-25 晚,逐文件探针)**,
# 但保留这段历史,因为**我当时的"闭不了"判断本身是错的**,而错法有教学价值:
# 当时(a)`strip_text` 尾接吞掉违规行 ∧(b)自检失败分支不可达 ⇒ rc=0 静默。
# 我判"要闭需一条独立于本文件的检查 ⇒ 无限后退 ⇒ 记为边界"。
# ★ 错在哪: 我把"独立"理解成"**另一个文件**",而真正需要的是
# "**另一次观察**" —— 逐文件探针(见上文)用**被检对象自己的形状**当样本,
# 它仍在本文件里,却**不经过自检那条判断**(直接看 `_scan_stripped` 的行号算术)。
# ⇒ 实测: 同一 ①∧② 合取,现在 rc=**1**(报"逐文件探针失败")。
# ⇒ 记法: **"无限后退"论证要先问"上一层与这一层是不是同一次观察"** ——
# 若新一层换了**观测对象**(从"已知样本"换成"被检文件本身"),它不是后退,是**换面**。
# 我当时把"换面"误判成了"后退",于是把一个可闭的点申报成了边界。
# ⑥ 间接赋值不判(eval/read/env 注入),只判**字面裸赋值**。
# ⑦ 下界守卫只挡"收到 <3 个调用者";"恰好 3 个但换掉其中一个"不判(需会漂的名单)。
# ⑧ ★ 逐文件探针的**射程**(实测申报,不是"没想到"): 它证明"读入是逐行原位的",
# 但**不能**证明"注释剥离的语义正确" —— 例如把 `sed 's/#.*$//'` 改成
# `sed 's/#.*$//'` 之外的**等价但不同**的规则(如只删整行注释)仍可能通过探针,
# 因为探针只查"行号与两端",不查"哪一列被删"。要判它需**按列/按语义**的对照。
# ══════════════════════════════════════════════════════════════════════════════