fix(pi-bridge): "开关被认"那条判据自己也有假绿 —— 我按真实测量分叉,于是永远走短路分支
pi 评审第二轮指出:上一版"开关真的被认"只在"真实测量不足"那个分支里断言,
机器一恢复健康(/tmp 被清空)这条就退化成"只验 --measure"的弱检查,
而它守的恰恰是"开关别静默失效"。
认下之后我做变异(把开关整个忽略掉、永远用真实测量)验证,**发现比这更糟**:
那条新写的判据**在变异下照样绿**。
原因是我写成了 `realAvail < MIN_FREE_BYTES ? (不足分支,只看退出码) : (充足分支)`,
而本机真实可用**就是 0** ⇒ 永远走不足分支;开关被整个忽略时,回退测量同样给
exit 2 ⇒ 断言通过。**"断言在,区分力不在"** —— 与 pi 点的是同一类病,
只是它藏在一个**跑不到的分支**里(嵌套三元短路),比"分支退化"更难看出来。
修法:不跟真实测量比,**让两个探针自己互为反面**,并断言**输出里的判定词**
(不只看退出码 —— 退出码可能与真实状态巧合相同):
探针 A:注入 1 字节 ⇒ exit 2 + 必须打印「< 需要」
探针 B:注入 128 MiB(>阈值)⇒ exit 0 + 必须打印「≥ 需要」
开关被忽略 ⇒ 两次都按真实测量给同一个答案 ⇒ 至少一条红。这个论证不依赖真实测量
是多少。变异自检实测:注入"忽略开关"的变异后,第 23、24、25 三条一起红。
顺带修一处**HEAD 里就带着的坏行**:第 162 行的 `test(..., () => {` 后面被塞进了
`// 覆盖…` 注释(上一次编辑吃掉了那个换行),整行不合法。这次一并拆回两行。
过程中我两次改坏文件(一次把手写 `replace` 的锚点算错、把"非法参数"那条整条删掉),
两次都靠 `git checkout HEAD -- <file>` 拉回重做 —— 这正是上一轮写进
`lib/env-error.mjs` 的那条纪律(变异/改写只对已提交文件做、还原只走 git)当场生效。
验证:`npm test` **475/475**;env-guard 单跑 30 条全过(含 24 号在两个探针下的双断言)。
This commit is contained in:
@ -115,37 +115,28 @@ test('端到端:退出码 2 与文案(两个方向都验,不依赖机器
|
||||
assert.match(good.stdout, /env-preflight/);
|
||||
});
|
||||
|
||||
test('★开关真的被认:按真实测量分叉,两个分支各有分辨力', () => {
|
||||
// pi 评审指出上一版的漏洞:原来只在"真实测量不足"那个分支里断言,
|
||||
// 机器一恢复健康(/tmp 被清空)这条就退化成"只验 --measure"的弱检查 ——
|
||||
// 而它守的恰恰是"开关别静默失效"。
|
||||
test('★开关真的被认:两个探针必须给出**相反**的判定与相反的关键词', () => {
|
||||
// pi 评审的漏洞一:上一版只在"真实测量不足"那个分支里断言 ⇒ 机器一恢复健康
|
||||
// (/tmp 被清空)这条就退化成弱检查,而它守的恰恰是"开关别静默失效"。
|
||||
//
|
||||
// 两个方向是**对偶**的,各守一个机器状态,所以按实测分叉、在两个分支里
|
||||
// 断言**相反**的方向 ⇒ 任何机器状态下都有分辨力,不会退化成弱检查:
|
||||
// 漏洞二(我做变异时撞上的,比漏洞一更隐蔽):按"真实测量"分叉的写法本身留了一个
|
||||
// 短路分支 —— 本机真实可用就是 0 ⇒ 永远走不足分支,而那个分支只看退出码;
|
||||
// 于是把开关**整个忽略掉**(永远用真实测量),断言**照样绿**。
|
||||
// "断言在,区分力不在" —— 与 pi 点的是同一类病,只是它藏在"跑不到的分支"里。
|
||||
//
|
||||
// 真实不足 ⇒ 注入大数必须放行 (若开关被忽略,回退测量 ⇒ 2 ≠ 0 ⇒ 红)
|
||||
// 真实充足 ⇒ 注入 0 必须 exit 2(若开关被忽略,回退测量 ⇒ 0 ≠ 2 ⇒ 红)
|
||||
//
|
||||
// 判据红的方式不一样,但**红**这件事在两种机器状态下都成立。
|
||||
const measured = runPreflight(['--measure=/tmp']);
|
||||
assert.equal(measured.status, 0, '--measure 只量一次并 exit 0');
|
||||
const m = /"availBytes":\s*(\d+|null)/.exec(measured.stdout);
|
||||
assert.ok(m, `--measure 必须回一个数或 null,实际:${measured.stdout.trim()}`);
|
||||
// 修法:**不跟真实测量比,让两个探针自己互为反面**,并断言**输出里的判定词**
|
||||
// (不只看退出码 —— 退出码可能与真实状态巧合相同):
|
||||
// 探针 A:注入 1 字节 ⇒ exit 2 + 必须打印「< 需要」
|
||||
// 探针 B:注入 128 MiB(>阈值)⇒ exit 0 + 必须打印「≥ 需要」
|
||||
// 若开关被忽略,两次都按**真实**测量给同一个答案 ⇒ 至少一条红。
|
||||
// 这个论证不依赖真实测量是多少。
|
||||
const tiny = runPreflight(['--inject-avail=1']);
|
||||
assert.equal(tiny.status, 2, '注入 1 字节必须 exit 2');
|
||||
assert.match(tiny.stderr + tiny.stdout, /<\s*需要/, '必须打印「不足」的判定');
|
||||
|
||||
if (m[1] === 'null') {
|
||||
// 量不到就**不能**分叉(既不知道机器是哪种状态)。明说并让判据红,
|
||||
// 而不是悄悄跳过 —— 跳过会把"失去分辨力"伪装成"验过了"。
|
||||
assert.fail('本机 /tmp 量不出可用空间,这条判据无法分叉 —— 别让它静默退化');
|
||||
}
|
||||
const realAvail = Number(m[1]);
|
||||
|
||||
if (realAvail < MIN_FREE_BYTES) {
|
||||
const injected = runPreflight(['--inject-avail=999999999']);
|
||||
assert.equal(injected.status, 0, '真实不足 + 注入充足 ⇒ 必须按注入的走(开关被认)');
|
||||
} else {
|
||||
const injected = runPreflight(['--inject-avail=0']);
|
||||
assert.equal(injected.status, 2, '真实充足 + 注入 0 ⇒ 必须按注入的走(开关被认)');
|
||||
}
|
||||
const plenty = runPreflight(['--inject-avail=134217728']);
|
||||
assert.equal(plenty.status, 0, '注入 128 MiB(> 32 MiB 阈值)必须放行');
|
||||
assert.match(plenty.stdout, /≥\s*需要/, '必须打印「充足」的判定(不是靠退出码近似)');
|
||||
});
|
||||
|
||||
test('★非法参数必须炸(exit 2),不能静默放行', () => {
|
||||
@ -159,7 +150,8 @@ test('★非法参数必须炸(exit 2),不能静默放行', () => {
|
||||
assert.equal(runPreflight(['--inject-avail=null']).status, 0, 'null 是合法值(没测到 ⇒ 放行)');
|
||||
});
|
||||
|
||||
test('测量层:读不到的目录 → null(这条判据不依赖机器状态,永远跑得了)', () => { // 覆盖"读不到 ⇒ null ⇒ 放行"那一支。端到端那条一旦被跳过,就只剩这条管它。
|
||||
test('测量层:读不到的目录 → null(这条判据不依赖机器状态,永远跑得了)', () => {
|
||||
// 覆盖"读不到 ⇒ null ⇒ 放行"那一支。端到端那条一旦被跳过,就只剩这条管它。
|
||||
assert.equal(measureAvailBytes('/definitely/not/here'), null);
|
||||
assert.equal(measureAvailBytes(''), null, '空串不是合法目录');
|
||||
assert.equal(measureAvailBytes(undefined), null, '不传也要能兜住');
|
||||
|
||||
Reference in New Issue
Block a user