bookworm-smart-assistant/scripts/poc/poc-h2-manifest-concurrent.js

139 lines
4.7 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
/**
* PoC H2: manifest 并发写入安全性
*
* 红队 red-team-logic A3 [HIGH]: hook 并发 JSON.stringify 整文件写撕裂
* 方案对比:
* M1. JSON 整写 (当前多 hook 做法) 期望: 撕裂
* M2. JSONL append-only + proper-lockfile 期望: 无撕裂
* M3. fs.appendFileSync 原子(小于 PIPE_BUF=4096) 期望: Linux 原子, Windows 部分保证
*
* 测试: 10 个子进程并发写 100 条记录, 读回检查总条数和完整性
* 产出: h2-report.json
*/
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const { spawn } = require('child_process');
const SANDBOX = path.join(__dirname, '..', '..', 'ai-delivery-pipeline', '_poc-sandbox');
const WORKER = path.join(__dirname, 'poc-h2-worker.js');
const REPORT = path.join(SANDBOX, 'h2-report.json');
function makeWorkerIfMissing() {
if (fs.existsSync(WORKER)) return;
const src = `
'use strict';
const fs = require('fs');
const mode = process.argv[2]; // 'jsonl' | 'json-full'
const target = process.argv[3];
const workerId = process.argv[4];
const count = Number(process.argv[5]) || 100;
function writeJsonlRecord() {
for (let i = 0; i < count; i++) {
const rec = JSON.stringify({ worker: workerId, i, ts: Date.now() }) + '\\n';
fs.appendFileSync(target, rec);
}
}
function writeJsonFullRewrite() {
for (let i = 0; i < count; i++) {
let arr = [];
try { arr = JSON.parse(fs.readFileSync(target, 'utf8') || '[]'); } catch { arr = []; }
arr.push({ worker: workerId, i, ts: Date.now() });
fs.writeFileSync(target, JSON.stringify(arr), 'utf8');
}
}
if (mode === 'jsonl') writeJsonlRecord();
else writeJsonFullRewrite();
`;
fs.writeFileSync(WORKER, src, 'utf8');
}
function runConcurrent(mode, target, workers = 10, perWorker = 100) {
if (fs.existsSync(target)) fs.unlinkSync(target);
if (mode === 'jsonl') fs.writeFileSync(target, '', 'utf8');
return new Promise((resolve) => {
let done = 0;
const started = Date.now();
for (let w = 0; w < workers; w++) {
const proc = spawn(process.execPath, [WORKER, mode, target, 'w' + w, perWorker], {
stdio: 'ignore', windowsHide: true,
});
proc.on('exit', () => {
if (++done === workers) resolve(Date.now() - started);
});
}
});
}
function verifyJsonl(target, expected) {
const raw = fs.readFileSync(target, 'utf8');
const lines = raw.split('\n').filter(Boolean);
let parsed = 0, corrupt = 0;
for (const L of lines) {
try { JSON.parse(L); parsed++; } catch { corrupt++; }
}
return { total: lines.length, parsed, corrupt, expected };
}
function verifyJsonFull(target, expected) {
let parsed = 0, corrupt = 0, final = null;
try {
final = JSON.parse(fs.readFileSync(target, 'utf8'));
parsed = Array.isArray(final) ? final.length : 0;
} catch (e) {
corrupt = 1;
}
return { finalCount: parsed, corrupt, expected, lost: expected - parsed };
}
async function main() {
fs.mkdirSync(SANDBOX, { recursive: true });
makeWorkerIfMissing();
const W = 10, P = 100, EXPECTED = W * P;
const jsonlTarget = path.join(SANDBOX, 'h2-jsonl.log');
const jsonFullTarget = path.join(SANDBOX, 'h2-jsonfull.log');
console.log('[H2] running jsonl append-only with', W, 'workers x', P, 'records...');
const t1 = await runConcurrent('jsonl', jsonlTarget, W, P);
const jsonlResult = verifyJsonl(jsonlTarget, EXPECTED);
console.log('[H2] jsonl elapsed', t1, 'ms', jsonlResult);
console.log('[H2] running json full-rewrite (vulnerable pattern)...');
const t2 = await runConcurrent('json-full', jsonFullTarget, W, P);
const jsonResult = verifyJsonFull(jsonFullTarget, EXPECTED);
console.log('[H2] json-full elapsed', t2, 'ms', jsonResult);
const report = {
hypothesis: 'H2 · manifest 并发写入安全性',
platform: os.platform() + ' ' + os.release(),
nodeVersion: process.version,
timestamp: new Date().toISOString(),
workers: W, perWorker: P, expected: EXPECTED,
jsonl_append_only: { elapsedMs: t1, ...jsonlResult },
json_full_rewrite: { elapsedMs: t2, ...jsonResult },
verdict: {
jsonlSafe: jsonlResult.corrupt === 0 && jsonlResult.parsed === EXPECTED,
jsonFullSafe: jsonResult.corrupt === 0 && jsonResult.lost === 0,
},
recommendation: '',
};
report.recommendation = report.verdict.jsonlSafe && !report.verdict.jsonFullSafe
? '✅ manifest 必须用 JSONL append-only, 禁用 JSON 整文件写'
: report.verdict.jsonlSafe
? '✅ JSONL 安全; 整写偶然未暴露但仍有风险, 强制 JSONL'
: '❌ JSONL 也撕裂, 必须加 proper-lockfile';
fs.writeFileSync(REPORT, JSON.stringify(report, null, 2), 'utf8');
console.log('\n' + JSON.stringify(report, null, 2));
}
main();