#!/usr/bin/env node /** * verify-jsonl-chain.js — JSONL 完整性链验证 * * 用法: * node scripts/patches/verify-jsonl-chain.js # 验证所有目标 * node scripts/patches/verify-jsonl-chain.js --quiet # 仅退出码 * node scripts/patches/verify-jsonl-chain.js --rebuild # 重建 baseline (信任当前) * * 退出码: * 0 = 全部 OK * 1 = 至少一个 drift 检测到(warn 模式仅记录,不阻断业务) * 2 = baseline 缺失(首次需运行 patch-p1-2-jsonl-baseline-init.js) * * warn 模式: 所有 drift 写入 evolution-log.jsonl 作为 jsonl-hmac.violation 事件 */ 'use strict'; const fs = require('fs'); const path = require('path'); const ROOT = path.join(__dirname, '..', '..'); const TARGETS = [ { jsonl: path.join(ROOT, 'evolution-log.jsonl'), label: 'evolution-log' }, { jsonl: path.join(ROOT, 'debug', 'route-feedback.jsonl'), label: 'route-feedback' }, ]; const QUIET = process.argv.includes('--quiet'); const REBUILD = process.argv.includes('--rebuild'); let mod; try { mod = require(path.join(ROOT, 'hooks', 'lib', 'jsonl-hmac.js')); } catch (e) { process.stderr.write('[FAIL] jsonl-hmac.js lib not deployed\n'); process.exit(2); } function log(s) { if (!QUIET) process.stdout.write(s + '\n'); } function err(s) { process.stderr.write(s + '\n'); } let exitCode = 0; const violations = []; for (const { jsonl, label } of TARGETS) { const baselinePath = jsonl + '.hmac-baseline.json'; if (!fs.existsSync(jsonl)) { log('[SKIP] ' + label + ': file not found'); continue; } if (REBUILD) { const r = mod.writeBaseline(jsonl, baselinePath); if (r.ok) { log('[REBUILD] ' + label + ' baseline updated lines=' + r.baseline.lineCount); } else { err('[REBUILD-FAIL] ' + label + ': ' + r.error); exitCode = 1; } continue; } if (!fs.existsSync(baselinePath)) { err('[NO-BASELINE] ' + label + ': run patch-p1-2-jsonl-baseline-init.js first'); if (exitCode === 0) exitCode = 2; continue; } const v = mod.verifyChain(jsonl, baselinePath); if (v.ok) { log('[OK] ' + label + ' baseline=' + v.baselineLineCount + ' current=' + v.currentLineCount + ' appended=' + v.appended); } else { err('[DRIFT] ' + label + ' drift=' + v.drift + (v.atLine ? ' atLine=' + v.atLine : '')); violations.push({ label: label, drift: v.drift, detail: v }); if (exitCode === 0) exitCode = 1; } } // warn 模式: 把 violation 记录到 evolution-log if (violations.length > 0) { try { const evtFile = path.join(ROOT, 'evolution-log.jsonl'); for (const v of violations) { const entry = { ts: new Date().toISOString(), type: 'jsonl-hmac.violation', target: v.label, drift: v.drift, detail: v.detail, mode: 'warn', }; // 追加 (不依赖 safe-append,避免循环) fs.appendFileSync(evtFile, JSON.stringify(entry) + '\n'); } err('[WARN-LOGGED] ' + violations.length + ' violations recorded to evolution-log.jsonl'); } catch (e) { err('[WARN-LOG-FAIL] ' + e.message); } } process.exit(exitCode);