bookworm-smart-assistant/scripts/poc/poc-h3-rollback.js

122 lines
4.4 KiB
JavaScript
Raw Permalink Normal View History

#!/usr/bin/env node
/**
* PoC H3: staging snapshot 回滚的正确性
*
* 场景:
* src/foo.ts 内容 = "version-1"
* Claude Edit 写入 "version-2-BAD" (模拟坏变更)
* PostToolUse hook 同步复制一份到 staging/<sessionId>/foo.ts (snapshot)
* validator 判定失败
* 回滚: file-history/ 找回 "version-1" 放回原路径; staging 版本 mv quarantine
*
* 验证点:
* 1. 回滚后 src/foo.ts 完全等于原始内容 (字节级)
* 2. UTF-8 BOM 保留
* 3. 二进制文件 (\x00 \xff) 不损坏
* 4. quarantine 版本可审计读回
*/
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const crypto = require('crypto');
const SANDBOX = path.join(__dirname, '..', '..', 'ai-delivery-pipeline', '_poc-sandbox');
function sha256(p) {
return crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex');
}
function scenario(name, originalContent, badContent, isBinary) {
const workDir = path.join(SANDBOX, 'h3-' + name);
fs.mkdirSync(workDir, { recursive: true });
const srcDir = path.join(workDir, 'src');
const historyDir = path.join(workDir, 'file-history');
const stagingDir = path.join(workDir, 'staging', 'session-x');
const quarantineDir = path.join(workDir, 'quarantine');
[srcDir, historyDir, stagingDir, quarantineDir].forEach(d => fs.mkdirSync(d, { recursive: true }));
const srcFile = path.join(srcDir, 'foo.bin');
// Step 1: 原始写入 + file-history 快照 (模拟既有能力)
fs.writeFileSync(srcFile, originalContent);
const historySnap = path.join(historyDir, 'foo.bin.v1');
fs.copyFileSync(srcFile, historySnap);
const originalHash = sha256(srcFile);
// Step 2: Claude Edit 写入坏版本
fs.writeFileSync(srcFile, badContent);
// Step 3: PostToolUse hook 同步 snapshot 到 staging (模拟)
const stagingSnap = path.join(stagingDir, 'foo.bin');
fs.copyFileSync(srcFile, stagingSnap);
// Step 4: validator 失败 → 回滚
// 4a. 从 file-history 恢复原文件
fs.copyFileSync(historySnap, srcFile);
// 4b. staging 版本 mv 到 quarantine
const quarantined = path.join(quarantineDir, 'session-x_foo.bin');
fs.renameSync(stagingSnap, quarantined);
// 验证
const restoredHash = sha256(srcFile);
const restoredContent = fs.readFileSync(srcFile);
const quarantineContent = fs.readFileSync(quarantined);
const bytesEqual = Buffer.compare(restoredContent, Buffer.isBuffer(originalContent) ? originalContent : Buffer.from(originalContent)) === 0;
const quarantineEqual = Buffer.compare(quarantineContent, Buffer.isBuffer(badContent) ? badContent : Buffer.from(badContent)) === 0;
const hashMatch = restoredHash === originalHash;
return {
scenario: name,
isBinary,
originalHash,
restoredHash,
hashMatch,
bytesEqual,
quarantineEqual,
pass: hashMatch && bytesEqual && quarantineEqual,
};
}
function main() {
fs.mkdirSync(SANDBOX, { recursive: true });
const results = [
scenario('text-ascii', 'function foo() { return 1; }\n', 'MALICIOUS\n', false),
scenario('text-utf8-bom', '中文内容\n测试', 'BAD\n坏内容', false),
scenario('text-lf-crlf-mix',
'line1\r\nline2\nline3\r\n',
'line1\nline2\r\nline3',
false),
scenario('binary-null-bytes',
Buffer.from([0x00, 0xff, 0x7f, 0x80, 0x01, 0xde, 0xad, 0xbe, 0xef]),
Buffer.from([0xba, 0xd0, 0xc0, 0xde]),
true),
scenario('empty-file', '', 'not-empty', false),
scenario('large-1mb',
Buffer.alloc(1024 * 1024, 0xab),
Buffer.alloc(1024 * 1024, 0xcd),
true),
];
const pass = results.filter(r => r.pass).length;
const report = {
hypothesis: 'H3 · staging snapshot 回滚的正确性',
platform: os.platform() + ' ' + os.release(),
nodeVersion: process.version,
timestamp: new Date().toISOString(),
totalScenarios: results.length,
passedScenarios: pass,
results,
verdict: { pass: pass === results.length, passRate: (pass / results.length * 100).toFixed(1) + '%' },
recommendation: pass === results.length
? '✅ 回滚逻辑字节级正确, 覆盖 6 种文件类型; 可接入生产 (需加 fsync 保证掉电安全)'
: '❌ 存在回滚失败场景, 见 results[].pass',
};
fs.writeFileSync(path.join(SANDBOX, 'h3-report.json'), JSON.stringify(report, null, 2), 'utf8');
console.log(JSON.stringify(report, null, 2));
}
main();