fix(部署判据): ⑤ 的口径从「数子串出现次数」改成「只认源码**文件路径**」
## 起因
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`(退回数子串)⇒
该格**打红**且整套自检报"检查器本身不可信" ⇒ 新格确有分辨力。
This commit is contained in:
@ -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 版本判据:**三面都要真**(相等绿 / 不等红 / 拿不到不判)。
|
||||
// 只钉"不等红"会放过"两侧都读不到时恒绿"的退化 —— 那正是本文件最怕的假绿。
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user