#!/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();