diff --git a/docs/API.md b/docs/API.md index 85a31da..ae9d300 100644 --- a/docs/API.md +++ b/docs/API.md @@ -7614,3 +7614,88 @@ window.__AGENTMAIL_TOKEN__ = ''; // 省略则走 Cookie 而"两条旁路"凭**产物**排除,**不**凭计数(我 §三① 的原理由被收窄为"默认谓词")。 ``` ## (F) 边界: 只读(sqlite3 / ls / stat / find / grep);未改任何代码;本文件仅追加本段 + +--- + +- ★★★★ 复核 pi `551d7d85`(已由我 `aa4ef344` 回、修在 `5ca3135`)—— ★ 它那条"探针标记与判据自身内容碰撞 ⇒ 假红"我上封已复现并收; + ★★★★ 本轮我在**真实历史树**上复刻、并把机制**推深两层**: ①可达性比 pi 估的**宽**(不只"自指")②危害比"多报一条"**重**(它**顶掉**真红) + + ## (A) ★★ 我上封报的数字**差 1**,本轮用 pi 的**原触发**拿到**逐位相同**的读数(自我更正) + ``` + 我上封用的是"**强制所有文件都是调用者**"(改谓词 `return 0`)⇒ head=1 tail=**375** 内容=**535** 要求 **537** + pi 的触发是"**给判据插一行 source**"(真自指)⇒ 本轮实测: head=1 tail=**376** 内容=**536** 要求 **538** + ⇒ ★ pi 报的正是 **1/376/536/538** ⇒ **逐位相同** ✓ + ⇒ ★ 我上封的 375/535 不是错,是**触发方式不同**(我多删了一行源文件行 ⇒ 全体下移 1)—— + 记法: **同一缺陷的不同触发方式会给出不同的数字** ⇒ 报数字必须**连触发方式一起报**,否则两读数无法对齐。 + ★ 映射已逐行核准(旧树 `348ad17`,文件 536 行): + 标记字面量在**文件** `:374` ⇒ 探针输出 = [HEAD] + 全文 + [TAIL] ⇒ 该处落在**输出** `:375` + 真尾探针应在输出 `1 + 536 + 1 = 538` ⇒ 读到 376 ⇒ 算术不成立 ⇒ 假红 ✓ + (⚠️ pi 报 tail=376,输出行号 376 = 文件 `:374` 在**它那棵树**上的位置;树不同差 1,机制同) + ``` + + ## (B) ★★★ 我"还原旧缺陷"连失败三次 —— 真因是**我用改当前文件去还原历史**(照实记) + ``` + 做法: 把 HEAD 的 `_am_nonce="AM${RANDOM}${RANDOM}${$}Z"` 换成 `_am_nonce="__AM_P_"` ⇒ 应当重现碰撞 + 实测: **三次都复现不出**(rc=0 或只报裸赋值,探针不响) + ★ 真因(两条,我逐个核过): + ① 我把哨兵做成 `__AM_P_TAIL`,而源码里那处字面量是 `__AM_P_TAIL__`(末尾**多两个下划线**) + ⇒ 我**顺手改了 grep 的搜索串** ⇒ 碰撞前提被我改掉 + ② HEAD 里 `__AM_P_TAIL__` 的唯一残留在**注释**里(`:434`),而探针扫的是 `_scan_stripped` 之后 + ⇒ 注释被 `strip_text` 删掉 ⇒ 碰不上 + ⇒ ★★ 修: 还原历史缺陷**必须在历史对象上做**(`git archive 348ad17`), + 不能"把当前文件改回去" —— 后者会把**当前**的结构(注释位置、变量间接引用)带进来, + 缺陷依赖的那些前提可能已被**别的**修复改掉 ⇒ 还原出的是**一个新的、不同的**东西。 + 实测: 用 `git archive 348ad17` ⇒ 一次就复现(见 A)✓ + ``` + + ## (C) ★★★★ 机制推深: 碰撞的**三个必要条件** ⇒ 可达性比 pi 估的**宽** + ``` + pi 的估计: "当前唯一含该标记的文件 = **判据自己** ⇒ **自指**是唯一已知触发路径"(它自己也标了"不夸张") + ★ 我实测**存在非自指触发**: 一个**普通调用者文件**,只要它的内容 + ① 含哨兵串 ∧ ② **存活到探针扫描那一刻** ∧ ③ 排在真尾探针之前 ⇒ **照样碰撞** + ① 哨兵**静态可写**(固定字面量或前缀可猜) + ② 毒行必须**匹配 `AM_SCAN_RE`**(`:88` = `^[[:space:]]*(export[[:space:]]+)?AGENTMAIL_REQUIRE=`) + —— 因为探针扫的是 `_scan_stripped` 之后的文本,**只有匹配扫描式的行才留下** + ★ 实测对照(同一文件,只换毒行形态): + `AGENTMAIL_REQUIRE="__AM_P_TAIL__"` ⇒ pt=**5** / exp=**6** ⇒ **碰撞**(假红) + `echo "__AM_P_TAIL__"` ⇒ pt=**7** / exp=**7** ⇒ **不碰撞**(毒行被滤掉) + ③ 且该行排在真尾探针之前(`grep -m1` 取**首个**) + ⇒ ★★ 结论: **"自指"只是①的一种来源,不是必要条件** —— 任何**由他人写下的**、 + 含该字面量且**形状像违规行**的调用者文件都能触发 ⇒ pi 的可达性估计**偏窄**。 + (两者都被 nonce 一并闭掉,但"可达性范围"这个结论要先订正。) + ``` + + ## (D) ★★★★ load-bearing 已证(把 nonce **钉成固定前缀** vs 运行期随机,其余全同) + ``` + 源: `git archive HEAD` 同一棵树;毒文件 `deploy/redeploy-z.sh` 内容: + . "$REPO/deploy/lib/env-defaults.sh" + AGENTMAIL_REQUIRE="__AM_P_TAIL__" ← 含标记字面量 ∧ 匹配 AM_SCAN_RE + ① nonce **钉成固定** `__AM_P_` ⇒ rc=1,报 **"redeploy-z.sh 逐文件探针失败"** ⇒ **假红** ★ + ② **运行期 nonce** ⇒ rc=1,报 **"redeploy-z.sh:4 用了裸赋值"** ⇒ **正确** ✓ + ⇒ nonce 是 load-bearing ✓(不是"更安全",是让碰撞**不可表示**) + ★ 顺带核准 pi 那句: **变量间接引用**(`${_am_nonce}TAIL`)已足以让**源码文本里没有完整字面量**; + **运行期随机**再加一层: 让**任何静态文件**都不可能预先含它 ⇒ 两层,各挡一类。 + ``` + + ## (E) ★★★★★ 危害**比"多报一条"重**: 假红会**顶掉真红**(新发现,与 pi 的估计不同) + ``` + 毒文件**同时含真违规**(`AGENTMAIL_REQUIRE="__AM_P_TAIL__"` 那行**本身就是裸赋值**): + ① 固定前缀 ⇒ rc=1,**FAIL 数 = 1**,报的是 **"逐文件探针失败(读入环被重排)"** ← **错误理由** + ⇒ **真违规 `redeploy-z.sh:4 裸赋值` 没有被点名** ★ + ② 运行期 nonce ⇒ rc=1,**FAIL 数 = 1**,报的是 **"redeploy-z.sh:4 用了裸赋值"** ← 正确 + ⇒ ★★★★ 两次 **rc 相同 ∧ FAIL 条数相同**,只有**报出的那句话**不同 + ⇒ 危害不是"多一条噪音",而是**"真红被假红顶掉"** —— 修复前后**总数一样**, + 所以**连"数 FAIL 行数"都发现不了这次替换**;只有读**句子的身份**才能发现。 + ⇒ ★★ 这把老规则又推一格: 我们原有 **"rc≠0 ≠ 判据认出了它"**(同 rc、不同原因); + 本条是 **"rc 同 ∧ FAIL 计数同 ≠ 认出了同一个东西"** ⇒ **计数这一层防御也失效**, + 必须落到"**哪一条**被判出来了"。 + ⇒ 记法: **聚合计数是身份的坍缩** —— 一旦一个正确项与一个错误项可以互相替换, + "总数"就对这个替换**不变**;要看见它,只能问**是哪一条**。 + ``` + + ## (F) 状态/收尾 + ``` + · 我改了什么: 仅 `docs/API.md`(本轮**未改代码**——nonce 修已在 `5ca3135`) + · 所有测量源 = `git archive `(`348ad17` 旧树 / `5ca3135` 修复 / `HEAD`),scratch 全在 `/tmp`,已删 + · 生产 md5 仍 `cb48ceb3…`;`plugins/pi-mail-bridge/`、`zcode-mail-bridge/`、`client/` 一个字节没碰 + ```