安全修复:复合命令缺口、状态文件投毒、自批命令、协议 fail-open、dangerlist 漏报

- COMPOUND 补全换行/单个&/重定向(白名单不再被多行/重定向命令绕过),find 移出白名单
- gatekeeper 状态文件读写确定性硬拒(防 approved.json 投毒绕过灾难黑名单)
- 模型自批 approve 命令确定性硬拒(防 --yes 自我放行)
- approvedConsume 校验时间戳必须在过去(防未来时间戳使批准永不过期)
- daemon 协议错误/未知 op 返回 {ok:false},客户端降级内联完整审查(消除 fail-open)
- onReviewerError 支持 prompt 别名;llmTimeoutMs 超时预算钳制(防 hook 超时 fail-open)
- dangerlist 补漏:del/rd flag 顺序无关、Remove-Item 盘根星号、Format-Volume、format.com
- review 输入 JSON 解析失败与基础设施崩溃改为 fail-closed
- install 首次安装从模板生成 config.json;回归扩展至 41 项
This commit is contained in:
luoxiaoxin123
2026-09-13 20:59:36 +08:00
parent d1188c8eef
commit b207dbcaa0
8 changed files with 99 additions and 20 deletions
+13 -3
View File
@@ -73,7 +73,9 @@ const server = net.createServer({ allowHalfOpen: true }, (socket) => {
socket.setTimeout(42_000, () => socket.destroy()); // 兜底:不允许超过 hook 超时
const dispatch = async (line) => {
let res = { exitCode: 0, stdout: '', stderr: '' };
// 协议异常/未知 op 一律返回 {ok:false}(无 exitCode 字段)——客户端据此降级内联做完整审查,
// 绝不能返回 {exitCode:0}(会被当作"明确放行"决策,形成 fail-open)。
let res = { ok: false, error: 'unknown op' };
try {
const req = line.trim() ? JSON.parse(line.trim()) : null;
if (!req || req.op === 'ping') {
@@ -85,7 +87,7 @@ const server = net.createServer({ allowHalfOpen: true }, (socket) => {
res = await handleHook(req);
}
} catch (e) {
res = { exitCode: 0, stdout: '', stderr: '' }; // 协议异常不放行也不拦截,交回内联/原生流程
res = { ok: false, error: 'request error: ' + (e?.message || e) };
log('request error: ' + (e?.message || e));
}
socket.write(JSON.stringify(res) + '\n', () => { try { socket.end(); } catch { /* 忽略 */ } });
@@ -127,6 +129,7 @@ server.listen(PORT, '127.0.0.1', () => {
body: JSON.stringify({ model: all.config.model, messages: [{ role: 'user', content: 'ping' }], max_tokens: 1 }),
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error('HTTP ' + res.status);
await res.json().catch(() => {});
llmWarm = true;
log('llm connection warmed in ' + (Date.now() - t0) + 'ms');
@@ -144,7 +147,14 @@ async function ping(port, timeoutMs) {
s.setEncoding('utf8');
const to = setTimeout(() => { s.destroy(); reject(new Error('timeout')); }, timeoutMs);
s.on('connect', () => s.write(JSON.stringify({ op: 'ping' }) + '\n'));
s.on('data', (c) => { buf += c; if (buf.includes('\n')) { clearTimeout(to); resolve(JSON.parse(buf)); s.end(); } });
s.on('data', (c) => {
buf += c;
if (buf.includes('\n')) {
clearTimeout(to);
try { resolve(JSON.parse(buf)); } catch { resolve(null); }
s.end();
}
});
s.on('error', (e) => { clearTimeout(to); reject(e); });
});
}