From 1639382eaa1eb5e1227a7b1594c03c2e41abed25 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 28 Sep 2026 08:43:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E9=83=A8=E7=BD=B2=E5=88=A4=E6=8D=AE):=20?= =?UTF-8?q?=E2=91=A4=20=E7=9A=84=E5=8F=A3=E5=BE=84=E4=BB=8E=E3=80=8C?= =?UTF-8?q?=E6=95=B0=E5=AD=90=E4=B8=B2=E5=87=BA=E7=8E=B0=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E3=80=8D=E6=94=B9=E6=88=90=E3=80=8C=E5=8F=AA=E8=AE=A4=E6=BA=90?= =?UTF-8?q?=E7=A0=81**=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84**=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 起因 trimpath 修好之后(67 处 → 0 处真源码路径),判据第 6 条**仍红 1 处**。 查下去发现那 1 处不是缺陷: server/internal/handler/mail.go:645 的 400 错误文案 —— 「请带上你所处工作区的绝对路径,例如 &workspace=/home/program/agentmail」 那是**给调用方看的示例值**,且它在二进制字符串表里紧邻下一条 SQL 字面量, 拼成 `…/agentmailINSERT INTO mails (session_id, …)` —— **看起来极像「路径 + 代码」,实际是两条无关的字符串常量相邻**。 ## ★★ 我上一轮把它误判成「测试夹具」 我grep 源码时命中的是 `notify_test.go` 里的 workspace 夹具, 就下了「是测试数据」的结论 —— 那是**另一个**字符串(`seedAdopted(t,"pi","pid-ws-1", "/home/program/agentmail")`),只是恰好也含 REPO。 **真正的来源是生产错误文案。** 先下结论再取证,又一次。 ## 口径改动 原口径 `text.split(REPO).length - 1` 数的是**子串出现次数**,把两件事混成一件: · 真缺陷:trimpath 没生效,产物里印着 `…/server/internal/repo/repo.go` · 误报: 源码里**本来就该有的字符串**恰好含这个子串 ⇒ 改成逐个出现位置看**后缀**:REPO 之后是**源码文件扩展名**才是真路径。 这条判据要抓的是"源码**文件位置**被泄露",扩展名正是它的形状; 而示例值后面跟的是 `INSERT`(SQL 关键字),不是文件。 ## 两条都要报(把两者混成一个数字正是原口径的毛病) bad = 真源码路径 ⇒ **判红** other = 还有别处出现但不是文件路径 ⇒ **只提示**,且**给出真实样例** 第一版的"只提示"那档输出了「样例:见下」而样例永远取不到值 (只给真路径留了样例)—— 一句指向不存在内容的指路词。已修。 ## 验证(两侧都用**真 26MB 二进制**,不是合成样本) 已部署(-trimpath) 源码路径=0 非文件字样=1 ⇒ 判绿 ✓ 本地构建(无 trimpath) 源码路径=66 非文件字样=1 ⇒ 判红 ✓ ## 自检 新增一格反面样本:`★网关二进制:非源码路径的仓库字样(示例值)不得误判红` (`--self-check` 47 → **48** 格)。 变异验证:把 `SRC_EXT.test(tail)` 改成 `true`(退回数子串)⇒ 该格**打红**且整套自检报"检查器本身不可信" ⇒ 新格确有分辨力。 --- deploy/check-deploy-drift.mjs | 84 ++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/deploy/check-deploy-drift.mjs b/deploy/check-deploy-drift.mjs index 2526b05..a393671 100644 --- a/deploy/check-deploy-drift.mjs +++ b/deploy/check-deploy-drift.mjs @@ -1293,22 +1293,75 @@ export function checkLayout(inject = {}) { } push('各服务的工作目录/可执行文件不在源码目录', badHosts.length === 0, badHosts.join(' ')); - // ⑤ 生产二进制里不得嵌源码路径(-trimpath)。 + // ⑤ 生产二进制里不得嵌**源码路径**(-trimpath)。 // // Go 默认把源文件的**绝对路径**编进二进制。2026-09-14 实测:换到标准目录部署之后, // `/opt/agentmail/agentmail-gateway` 里仍有 57 处 `/home/program/agentmail/…` —— // 构建脚本漏了 `-trimpath`(对照实验:同一份源码、同一个 go,带标志 0 处、不带 57 处)。 // 这条是"运行时不再依赖源码目录"的**后半句**:依赖确实没了,但源仓库位置还印在产物上, // 而且它会把"这个二进制是从哪份源码建的"变成只能靠推断的事。 + // + // ★★★ 口径必须是「**源码文件路径**」,不能是「出现 REPO 这个子串」(2026-09-28 修正)。 + // + // 原口径 `text.split(REPO).length - 1` 数的是**子串出现次数**,它把两件 + // 不同的事混成一件: + // · 真缺陷:trimpath 没生效,产物里印着 `…/server/internal/repo/repo.go` + // · 误报: 源码里**本来就该有的字符串**恰好含这个子串 + // + // 实测的误报(部署已修完 trimpath 之后仍红 1 处): + // server/internal/handler/mail.go:645 的 400 错误文案 —— + // 「请带上你所处工作区的绝对路径,例如 &workspace=/home/program/agentmail」 + // 那是**给调用方看的示例值**,且它后面在二进制里紧邻着下一条 SQL 字面量 + // (字符串表把两者拼在一起:`…/agentmailINSERT INTO mails …`), + // 看起来极像路径 + 代码,实际是两条无关的字符串常量相邻。 + // + // ⇒ 改成只认「REPO 之后跟着一个**源码文件扩展名**」。 + // 这条判据要抓的是"源码**文件位置**被泄露",文件扩展名正是它的形状; + // 而示例值后面跟的是 `INSERT`(一个关键字),不是文件。 + // + // 两条都要报:bad = 真路径(判红),other = 还有别处出现(**只提示**)。 + // 把两者混成一个数字正是原口径的毛病 —— 那个 1 让我上一轮误判成"测试夹具"。 const BIN = '/opt/agentmail/agentmail-gateway'; + // 本仓会编进 Go 二进制的源码文件扩展名。刻意只列这一小撮: + // 列全了反而会被示例值里的点号骗到,而"漏了新语言"的后果是漏报(本就在 0 附近)。 + const SRC_EXT = /\.(go|ts|tsx|ets|mjs|cjs|js|jsx|json|md|sh|py|yaml|yml|sql|css|html|proto)\b/; let binHits = -1; + let binOther = -1; let binNote = `读不到 ${BIN}(标准位置没有网关二进制)`; try { const text = String(readFile(BIN)); - binHits = text.split(REPO).length - 1; - binNote = binHits === 0 - ? `${BIN} 里一处都没有` - : `${BIN} 里有 ${binHits} 处 ${REPO}/…(重新构建即可清零:跑一次 redeploy-gateway.sh)`; + // 逐个出现位置看**后缀**:是源码扩展名 ⇒ 真路径;否则只是子串巧合。 + let bad = 0; + let other = 0; + const samples = []; + let at = text.indexOf(REPO); + while (at !== -1) { + const tail = text.slice(at + REPO.length, at + REPO.length + 80); + if (SRC_EXT.test(tail)) { + bad++; + if (samples.length < 3) samples.push(tail.split(/[^\w./-]/)[0]); + } else { + other++; + // ★ 也要留样例:第一版只给真路径留样例,而"只提示"那一档输出的 + // 「样例:见下」永远取不到值 —— 一句指向不存在内容的指引。 + // (判据自己的注释写过「不能写成『文件名是句子里的一段』」, + // 同一个毛病也出现在「样例:见下」这种指路词上。) + if (samples.length < 3) { + const stop = tail.search(/[^\w./\-&?=]/); // 在分隔符处停(- 需转义,否则当成范围) + samples.push(stop > 0 ? tail.slice(0, stop) : tail.slice(0, 40)); + } + } + at = text.indexOf(REPO, at + 1); + } + binHits = bad; + binOther = other; + binNote = bad === 0 + ? (other === 0 + ? `${BIN} 里一处都没有` + : `${BIN} 里没有源码路径;另有 ${other} 处出现仓库路径字样但**不是文件路径**` + + `(示例值之类,样例:${samples[0] || '(未取到)'}` + (other > 1 ? ` 等 ${other} 处` : '') + `)—— 只提示,不判红`) + : `${BIN} 里有 ${bad} 处源码路径 ${REPO}/…(重新构建即可清零:跑一次 redeploy-gateway.sh)` + + (samples.length ? `;样例:${samples.join('、')}` : ''); } catch { /* binHits 保持 -1 = 读不到 */ } push('已安装的网关二进制不含源码路径(构建带 -trimpath)', binHits === 0, binNote); @@ -1578,6 +1631,21 @@ export function layoutSelfCheck() { '/opt/agentmail/bin/service-failure-notify.mjs': 'x', '/opt/agentmail/agentmail-gateway': 'ELF…github.com/agentmail/gateway/cmd/server…' })); + // ★★ 反面样本(2026-09-28 新增):trimpath **已生效**,但二进制里仍有 + // 一处仓库路径字样 —— 来自 handler/mail.go:645 的 400 错误文案 + // 「例如 &workspace=/home/program/agentmail」,而它在字符串表里紧邻着 + // 下一条 SQL 字面量,拼成 `…/agentmailINSERT INTO mails …`。 + // + // 这**不是**源码路径(后面跟的是 SQL 关键字,不是文件)⇒ 必须判**绿**。 + // 原口径数的是「REPO 子串出现次数」,会把这一处算进去 ⇒ **trimpath 修好后仍长期假红**。 + // (我上一轮据此误判成「测试夹具」,实际是生产错误文案 —— 又一次「先下结论再取证」。) + const goodButMentionsRepo = checkLayout(fake({ + '/etc/systemd/system': [], + '/repo/systemd': [], + '/opt/agentmail/bin/service-failure-notify.mjs': 'x', + '/opt/agentmail/agentmail-gateway': + 'ELF…例如 &workspace=/home/program/agentmailINSERT INTO mails (session_id…' + })); const dirty = checkLayout({ ...fake({ '/etc/systemd/system': [], '/repo/systemd': [], @@ -1713,6 +1781,12 @@ export function layoutSelfCheck() { // 二进制那条两侧都要真:嵌了源码路径必须红,trimpath 的必须绿。 { name: '网关二进制:嵌了源码路径必须判红', ok: fifth(badBin)?.ok === false }, { name: '网关二进制:trimpath 过的必须判绿', ok: fifth(good)?.ok === true }, + // ★ 那处**不是**源码路径的仓库路径字样(错误文案里的示例值)必须判绿 —— + // 没有这一格,口径就会退回「数子串出现次数」,而 trimpath 修好后长期假红。 + { + name: '★网关二进制:非源码路径的仓库字样(示例值)不得误判红', + ok: fifth(goodButMentionsRepo)?.ok === true + }, // ⑤b 版本判据:**三面都要真**(相等绿 / 不等红 / 拿不到不判)。 // 只钉"不等红"会放过"两侧都读不到时恒绿"的退化 —— 那正是本文件最怕的假绿。 {