agent-exec transport 5de4663cdba47dfb08c7f3ea2b74250e/484836365c2e

This commit is contained in:
agent-exec-transport
2026-10-21 03:58:43 +00:00
parent 6ddbae089f
commit 70c49470a6
@@ -0,0 +1,137 @@
# 最终独立审查:fix/guard-test-blindspot-coverage(终审)
- 审查对象:`7a5c80eacb835bfc6e62b3557129ec299bbb5290..HEAD`(分支 `fix/guard-test-blindspot-coverage`,HEAD=`6ddbae089f61dcda8d74d7f5980fb81631e30ac5`,经 `git rev-parse` 确认即分支尖端)
- 涉及文件:`tests/test_shell_pipe_hazard.py`(+406/−44)、`scripts/e2e-duties.sh`(2 处)、`scripts/hooks/pre-commit`(1 处)、`scripts/svc.sh`(1 处)、任务卡与首轮评审文档(`.task/**`,host 豁免路径)
- 审查身份:全新独立会话,未参与实现;只读审查,除本报告外未修改任何文件;合成脚本与探针全部位于 /tmp,仓库内无残留
- 审查日期:2026-10-21
## 结论:BLOCKED
历次评审的全部阻塞项(首轮 B1 子壳/花括组包裹、B2 awk nextfile、B3 while read break、绝对路径与 exec 前缀;第二轮 mapfile/readarray -n 与 read -n)在当前 HEAD 已全部闭合并有运行时 141 实证;四处脚本改写语义等价、发现机制稳定、契约表述诚实。但按本轮判据第 (2) 条与可证伪约束,实测构造出 **2 个此前未覆盖、运行时确认 pipefail 下 141、检测器不报** 的 **while/until 已声明命令族内其它停止写法**:
1. `seq 1 200000 | until read -r l; do :; done`(条件 read 首行成功即终止循环,无 break);
2. `seq 1 200000 | while read -r l; do exit; done` 及条件形 `[ "$l" = 7 ] && exit`(循环体以 exit 终止读取级子壳,无 break)。
按本轮约束「构造至少一个此前未覆盖的早退读取器形态……如报不出,作为阻塞项」,判 BLOCKED。不推翻已闭合项与等价性结论。
---
## 历次评审闭合核验(全部闭合,含独立复测)
| 历次阻塞项 | 闭合证据(本轮独立复测) |
|---|---|
| R1-B1 子壳/花括组包裹 `\| (head -1)`、`\| { head -1; }` | 检测器报 `(1,'(head -1)')`/`(1,'{ head -1; }')`;pipefail 实测 141。且帧设计自然覆盖组内嵌套管道:`seq \| (cat \| head -1)`、`seq \| { cat \| head -1; }`、`seq \| ( ( head -1 ) )` 均报,实测均 141 |
| R1-B2 awk nextfile | `awk '$1 == 7 { print; nextfile }'` 报;141 实测(套件 test_wrapped_and_prefixed… 内含 bash 运行时断言) |
| R1-B3 while read + break(含 break N、brace 内 break) | `while read -r l; do break; done`、`do break 2; done`(嵌套 for)、`do { break; }; done` 均报,实测 141;`do (break); done` 不报且实测 rc=0——子壳 break 本就不出外层循环,判定正确(无误报) |
| R1 绝对路径 / exec 前缀 | `\| /usr/bin/grep -q 1`、`\| exec grep -q 1`、`\| exec /usr/bin/head -1`、`\| /usr/bin/env LC_ALL=C grep -q 1` 均报 |
| R2 mapfile/readarray -n | `-n1`、`-t -n 3`、`-tn2`、`-u 0 -n 1`、`--n 1`、`--numlines 1`、`builtin mapfile -n 1`、`{ mapfile -n 1 arr; }` 均报;套件对 `-n 1`/`read -n 5` 附带 bash 运行时 141 断言并通过 |
| R2 read -n | `read -n 5 -r line`、`read --chars 5 line` 均报(read 族「任意用法」) |
## 重点核验(按本轮 5 项)
### 1. READER_FAMILIES 七族自检与族外不误报 — 通过
- `READER_FAMILIES`(tests/test_shell_pipe_hazard.py:23)7 族非空三元组;`test_declared_reader_families_each_flagged`(:548)以 `cases` 字典逐族断言「声明族必有被命中合成用例」,缺族/空字段即失败——契约↔测试 linkage 成立,套件 11/11 通过。
- 族外安全写法不误报:套件 33 行安全清单通过;本轮追加探针 `seq \| (cat)`、`seq \| env FOO=1 command cat`、`seq \| LC_ALL=C grep -c 1`、`seq \| mapfile -t arr`、out-of-family 读者 `dd`/`jq -n`/`perl -ne`/`xargs -n1` 全部不报。两处既有安全方向误报(awk 引号串 "exit"、算术按位或)见 N1/N3,均不涉当前受管脚本。
### 2. 族内同族写法漏检 — 发现 2 个阻塞项(见下),其余族闭合
- grep:`_grep_exits_early`(:336)**不因操作数停扫**(非选项词仅跳过),故 `grep 1 -q`、`grep 1 --quiet`、`grep 1 -m1`、`grep -e 1 -q`、`grep -f file -q 1`、`grep -d skip -q 1` 全部报;grep 无 -q/-m 之外的提前退出选项。族内无漏检。
- sed:`[qQ][0-9]*` 覆盖 `q`/`Q`/`qN`(`sed 'q2'` 实测即带退出码 2 的立即退出,被报);GNU sed 无其它停止命令。词法不可见的 `-f progfile` 见 N7。
- awk:END 块剥离后查 `exit|nextfile`,BEGIN 内 exit 报、仅 END 内不报(读满输入,安全)——与声明一致。
- head/read:任意用法全报。
- mapfile:`-n` 簇内/独立/长选项全覆盖;词序倒置 `mapfile arr -n 1` 不报但 bash 内建 getopt 停于首操作数、`-n` 不生效,实测 rc=0 读满输入——惰性写法,非危害(N4)。
- **while/until:除 break 外仍有两种停止写法漏检 → B1/B2 阻塞。**
### 3. 发现机制与受管脚本扫描 — 通过
- `managed_scripts()`(:51)经 `git ls-files -z -- ':(glob)scripts/**/*.sh' ':(glob)scripts/hooks/*' + 2 个 EXTRA`,本轮实测发现 17 个文件(含 3 个无扩展名 hooks);`scripts/hooks/pre-commit` 无扩展名仍被 `:(glob)scripts/hooks/*` 命中。
- 稳定性:`_lex`+`hazards` 显式跑遍 17 个脚本,0 解析错误、0 命中(与 `test_managed_scripts_have_no_early_exit_pipe_reader` 一致);`EXEMPT={}` 且有过期条目守卫(:613)。
- ignored 陈旧克隆不入扫描:`git ls-files` 只列跟踪文件 + `.gitignore:9` `runtime/`(`runtime-backups/:10` 同理);测试断言无 `runtime/` 前缀路径。本 worktree 无 runtime/ 目录,机制上也不可能漏入。
- 非阻塞残留(N2,首轮已提、本轮复核仍在):`scripts/agent-exec`、`scripts/dm`、`scripts/task`(无扩展名 bash)与 `docker/entrypoint.sh` 不在 glob 范围;旧硬编码列表同样未含,无回归。
### 4. 四处脚本改写语义等价 — 通过(含写路径守卫)
探针(/tmp,同输入同状态对比,全部 SAME):
| 改写 | 状态覆盖 | 结果 |
|---|---|---|
| `ps … \| grep -Fq` → `grep -Fq < <(ps …)`(e2e-duties.sh:87 cleanup) | 活进程匹配(exec -a 伪造 argv)→ 双方 matched-kill;活进程不匹配 → no/no;死 PID → no/no | 三态等价 |
| `curl … \| head -c 160` → `head -c 160 < <(curl …)`(e2e-duties.sh:128 log) | 1000B 取前 160B(165 字符含 `\|rc=` 完全一致)、短体、生产者失联 → 三态输出与 rc 等价(此处替换状态本就不被消费) | 等价 |
| `echo "$branch" \| grep -qE` → `grep -qE <<<"$branch"`(pre-commit:9) | 13 组分支名(4 合法 + feat/x、feature/、feature//x、含空格、空串、大写、`fix/-n pwned`、换行名)退出码逐一相同 | 写路径守卫未削弱:合法分支照常 admit,非法照旧拒绝且文案不变;`git rev-parse --abbrev-ref` 的 detached="HEAD" 两侧同拒;here-string 尾随换行与 echo 一致 |
| `postgres --version \| awk …exit` → `awk … < <(postgres --version) \|\| true`(svc.sh:4360 pgver) | 版本正常(24.1/24.1)、二进制失败(空/空)、空输出(空/空)、二进制缺失(空/空) | 捕获值与替换状态恒 0 两侧一致(`\|\| true` 保留) |
三脚本 `bash -n` 通过、均 `#!/usr/bin/env bash`(`<()`/`<<<` 合法);命令替换状态来源变化(head/awk 而非管道)在两处分别「不被消费」与「被 `\|\| true` 归零」,无行为差异。
### 5. 契约表述与实现一致 — 通过(无夸大承诺)
- docstring 明示「守护的承诺是『已声明的各族在发现到的脚本中处处被拦截』,而非『理解任意 bash 命令』」——承诺范围诚实收窄。
- `READER_FAMILIES` 7 条描述与实现逐条吻合:grep -q/-m(含 egrep/fgrep、长选项与 `=`)↔ `_grep_exits_early`;head 任意 ↔ 短路 True;sed q/Q ↔ `SED_QUIT`;awk END 外 exit/nextfile(含 gawk/mawk/nawk 别名)↔ `_awk_exits_early`;read 任意 ↔ True;mapfile `-n/--n/--numlines`(含 readarray)↔ `_mapfile_exits_early`;while「循环体 break 出本循环(含 until)」↔ `_lex` loops + `break_out`(brace 透明、子壳 `)` 正确截断、break N 层级计数)。
- 测试名与内容相符;「…flagged_and_harmful」用例真实执行 bash 并断言 141,非纸面断言。
- 保留意见:while/until 族触发词限定为「break」是**如实**描述实现,但该族的其它停止写法(条件终止、体中 exit)落在声明触发词之外而命令族之内——正是本轮第 (2) 条要求排查的形态(见阻塞项)。
---
## 阻塞项
### B1 检测器漏检:`until` 循环条件 read 首行成功即终止(无 break)
- 形态:`seq 1 200000 | until read -r l; do :; done`。`until` 条件为 read:首行读取成功 → 循环即结束 → 读取级子壳退出、stdin 关闭。语义等价 `head -1` 的纯 bash 写法,属已声明 while/until 命令族的另一种停止写法(触发机制是条件成功而非 break)。
- 运行时:`bash -c 'set -o pipefail; seq 1 200000 | until read -r l; do :; done'` → **141**(/tmp 实测)。
- 检测器实测:`hazards(...)` → `[]`。`early_exit_reader`(:392)对 while/until 仅查 `_lex(s)[1]` 的 `broken`(break 置位),`break_out`(:113)只认 `break` 命令。
- 影响:`until read`/`until IFS= read -t …` 形态可使缺陷类静默回流;当前 17 个受管脚本无此写法(`git grep "until "` 仅命中 ImageMagick `-until` 参数),属未来回归面。
### B2 检测器漏检:`while`/`until` 循环体内 `exit` 终止读取(无 break)
- 形态:`seq 1 200000 | while read -r l; do exit; done` 与惯用条件形 `… do [ "$l" = 7 ] && exit; done`。「读到目标行即退出」是常见习语;管道末级 while 在子壳中运行,exit 直接终止子壳、关闭 stdin。
- 运行时:两形态 `set -o pipefail` 下均 **141**(/tmp 实测)。
- 检测器实测:两形态均 `[]`——同 B1,`break_out` 不识别 `exit`。
- 影响:同族「读满即停」的第二个入口,词法可静态识别(循环体内命令位的 `exit`),未覆盖即构成本轮可证伪条款下的阻塞。
- 修复方向(供实施者参考,本轮未动代码):`break_out` 对循环帧内命令位的 `exit`/`exec <cmd>` 置 `broken`;`until` 循环在条件含声明族读者(read/head/…)时视为早退(`while` 条件读者则读满,安全,现判定已正确)。是否扩展触发词到 `return`(函数内管道级不可见,无意义)由实施者裁量。
两阻塞项均为「同一命令族(while/until)、运行时确认真实致害(141)、检测器不报」,满足本轮约束「如报不出,作为阻塞项」;亦满足首轮确立的「漏检使缺陷类可回流 = 阻塞」判据。合并修复面小(`break_out` + until 条件判定各一处),不影响其余已闭合结论。
## 非阻塞建议
- **N1(沿首轮)**:`seq 1 200000 | awk '{print "exit"}'` 被误报——`AWK_EXIT` 对程序文本裸 `\bexit\b`,引号串未剥离。安全方向;当前受管脚本无触发。建议 `_awk_exits_early` 剥引号串后再查。
- **N2(沿首轮)**:发现缺口——`scripts/agent-exec`、`scripts/dm`、`scripts/task`(无扩展名 bash)与 `docker/entrypoint.sh` 不被 `:(glob)scripts/**/*.sh`/`:(glob)scripts/hooks/*`/EXTRA 命中。建议增补 `:(glob)scripts/*` 与 `:(glob)docker/*.sh`。
- **N3(沿首轮)**:算术 `$((0|read))` 的 `\|` 被当管道拆级,操作数恰为 `read`/`head` 时误报。极牵强、安全方向,可忽略。
- **N4**:`mapfile arr -n 1`(选项在操作数后)不报——bash 内建 getopt 停于首操作数,`-n` 不生效,实测 rc=0 读满输入,惰性无害;可加注释说明词序豁免理由。
- **N5**:包裹前缀白名单 `PREFIXES`(:45)未含 `nohup`/`timeout`/`nice`/`stdbuf` 等 exec 型包装命令;`seq 1 200000 | timeout 5 head -1`、`| nohup head -1` 实测 141 且不报。属已声明「前缀」机制的可扩展清单之外(声明为「env/exec 等前缀」枚举),按可扩展契约记观察,不阻塞。
- **N6(按可证伪边界记录)**:族外读者 `dd`(`| dd bs=1 count=1` 实测 141)、`jq -n`、`perl -ne 'exit if …'`、`xargs -n1`、`select` 循环(`| select x in a; do break; done` 实测 141)均不报——非 READER_FAMILIES 声明族,依本轮明确边界只记非阻塞观察。
- **N7**:`sed -f prog.sed`/`awk -f prog.awk` 的程序文本在外部文件,词法不可见;与函数间接调用同级,契约「理解任意 bash 命令」之外,记观察。
## 判定标准对照
- 检测器漏检使该缺陷类可回流:**成立(B1/B2,运行时确认 141)→ 阻塞**。
- 七族自检 + 族外不误报:成立(第 1 节)。
- 发现机制稳定、无解析错误、ignored 陈旧克隆不入扫描:成立(第 3 节)。
- 改写引入语义回归:未发现(4/4 等价,含写路径守卫 13 组分支名探针)。
- 写守卫被削弱:未发现。
- 契约夸大承诺:未发现(承诺已按「已声明族 + 已发现脚本」收窄);while/until 触发词如实标注 break,B1/B2 按同族停止写法计阻塞而非虚言。
- 族外新命令族/构造:仅记 N5/N6/N7 非阻塞观察(边界条款)。
## 已运行的验证命令与结果(均只读;合成物全部位于 /tmp)
| # | 命令 | 结果 |
|---|---|---|
| 1 | `git rev-parse HEAD fix/guard-test-blindspot-coverage` | 二者同为 6ddbae089f61…;范围 `7a5c80e..HEAD` 即分支全量(diff --stat 6 文件) |
| 2 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` | **Ran 11 tests, OK** |
| 3 | `git ls-files -z -- ':(glob)scripts/**/*.sh' ':(glob)scripts/hooks/*' docker/verify-env.sh tests/load/collaboration/run_pgbench.sh` | 17 文件(含 3 个 hooks 无扩展名脚本,无 runtime/) |
| 4 | `head -1` + `bash -n`:scripts/e2e-duties.sh、scripts/hooks/pre-commit、scripts/svc.sh | 均 `#!/usr/bin/env bash`,bash -n 全过 |
| 5 | /tmp 探针:检测器注入形态矩阵(grep 词序/参数选项、until-read、while-exit、组嵌套管道、mapfile 词序、sed qN、前缀包装、族外读者、$((算术\|))、awk 引号串) | 报:grep 全词序形态、组嵌套管道 `(cat\|head -1)`/`{ cat\|head -1; }`/`( (head -1) )`、`while :; do read && break; done`、`sed 'q…'`;**不报:until-read-nobreak、while+exit(B1/B2)**、`(break)`(正确,实测 0)、mapfile 词序(实测 0)、族外读者、nohup/timeout 前缀;误报:awk 'exit' 串、`$((0\|read))` |
| 6 | /tmp pipefail 运行时基线(`bash -c 'set -o pipefail; …'`) | until-read、while+exit(无条件/条件)、`(cat\|head -1)`、`{ cat\|head -1; }`、`( (head -1) )`、`{ break; }`、`read && break`、`grep 1 -q`、`dd count=1`、`select …break`、`timeout 5 head -1`、`nohup head -1` → **141**;`while :`/`(break)`/`read \|\| break`/`mapfile arr -n 1`/`mapfile arr` → 0 |
| 7 | /tmp 等价性探针(pre-commit 13 分支名、cleanup 3 态含真实匹配 PID kill、head -c160 3 态、pgver 4 态) | 全部 SAME |
| 8 | 模块化扫描:`_lex`+`hazards` 遍历 17 个受管脚本 | errors=0,found=[] |
| 9 | `git grep "until "`、`\| while` 于受管脚本 | 无 until 管道读者;7 处 `\| while` 循环体均读满输入(printf/continue),无 exit/break 于读取级 |
| 10 | `grep -n runtime .gitignore`、`git check-ignore` 语义复核 | `.gitignore:9 runtime/`(:10 runtime-backups/),ignored 克隆不可能入 `git ls-files` |
| 11 | `sed 'q2'`/`sed '5q2'`/`sed '5q 2'` 行为定性 | `q2` 即退出码 2 立即退出(检测器 `[qQ][0-9]*` 命中);空格形惰性(rc=0),不构成漏检危害 |
## 可证伪性注入记录(约束第 4 条)
本轮人为注入 5 组此前套件未覆盖的族内新写法:`grep 1 -q`(词序倒置)→ 报 ✓;`grep -e 1 -q`(参数选项后置)→ 报 ✓;`(cat | head -1)`(组内嵌套管道)→ 报 ✓;`until read -r l; do :; done` → **不报 ✗**;`while read -r l; do exit; done` → **不报 ✗**。检测器对注入并非全盘照收,失败集中于 while/until 停止写法一处机制(`break_out` 仅认 `break`),修复面集中。按约束,不报者计阻塞项。
## 未验证事项
- 本报告全部结论基于本地只读命令与 /tmp 合成探针;未运行 CI、未做 REPLAY(审查单元不要求)。
- N5 中 `nice`/`stdbuf` 未逐个运行时实测(nohup/timeout 已实测 141),归类依同一 exec 包装机制推断。