92 lines
3.2 KiB
JavaScript
92 lines
3.2 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* PoC H4: 新增 sensitive-paths 规则与生产流水的兼容性
|
|||
|
|
*
|
|||
|
|
* 验证:
|
|||
|
|
* - 新 3 条 ai-delivery-pipeline 规则能正确匹配目标路径
|
|||
|
|
* - 不会误伤其他正常文件
|
|||
|
|
* - rules-compiled.json 已包含新规则
|
|||
|
|
* - hook 内部(持有 bypass token)可绕过保护写入 staging/
|
|||
|
|
*/
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const os = require('os');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const ROOT = path.join(__dirname, '..', '..');
|
|||
|
|
const SANDBOX = path.join(ROOT, 'ai-delivery-pipeline', '_poc-sandbox');
|
|||
|
|
const SP = path.join(ROOT, 'hooks', 'rules', 'sensitive-paths.json');
|
|||
|
|
const COMPILED = path.join(ROOT, 'hooks', 'rules', 'rules-compiled.json');
|
|||
|
|
|
|||
|
|
function loadPatterns() {
|
|||
|
|
const sp = JSON.parse(fs.readFileSync(SP, 'utf8'));
|
|||
|
|
return sp.patterns.map(p => ({ re: new RegExp(p.regex, p.flags || ''), reason: p.reason }));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function matches(patterns, p) {
|
|||
|
|
const hits = patterns.filter(x => x.re.test(p));
|
|||
|
|
return hits.map(h => h.reason);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function main() {
|
|||
|
|
fs.mkdirSync(SANDBOX, { recursive: true });
|
|||
|
|
const patterns = loadPatterns();
|
|||
|
|
|
|||
|
|
// 应命中的路径 (true positive)
|
|||
|
|
const shouldHit = [
|
|||
|
|
'C:/Users/leesu/.claude/ai-delivery-pipeline/staging/session-x/foo.ts',
|
|||
|
|
'C:\\Users\\leesu\\.claude\\ai-delivery-pipeline\\quarantine\\bad.ts',
|
|||
|
|
'/c/Users/leesu/.claude/ai-delivery-pipeline/delivery/ok.ts',
|
|||
|
|
];
|
|||
|
|
// 不应命中的路径 (true negative)
|
|||
|
|
const shouldMiss = [
|
|||
|
|
'C:/Users/leesu/projects/mybiobb.com/src/app/page.tsx',
|
|||
|
|
'C:/Users/leesu/.claude/skills/developer-expert/SKILL.md',
|
|||
|
|
'/home/user/ai-delivery-pipeline/staging/foo.ts', // 不在 .claude/ 下
|
|||
|
|
'C:/Users/leesu/.claude/ai-delivery-pipeline/README.md', // 根目录 README 不匹配 staging/
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const hitResults = shouldHit.map(p => {
|
|||
|
|
const reasons = matches(patterns, p);
|
|||
|
|
const passed = reasons.length > 0;
|
|||
|
|
return { path: p, expected: 'hit', reasons, pass: passed };
|
|||
|
|
});
|
|||
|
|
const missResults = shouldMiss.map(p => {
|
|||
|
|
const reasons = matches(patterns, p);
|
|||
|
|
const passed = reasons.length === 0;
|
|||
|
|
return { path: p, expected: 'miss', reasons, pass: passed };
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// rules-compiled 是否包含新规则
|
|||
|
|
const compiled = fs.readFileSync(COMPILED, 'utf8');
|
|||
|
|
const compiledHas = compiled.includes('ai-delivery-pipeline');
|
|||
|
|
|
|||
|
|
const allTests = [...hitResults, ...missResults];
|
|||
|
|
const pass = allTests.filter(r => r.pass).length;
|
|||
|
|
const total = allTests.length;
|
|||
|
|
|
|||
|
|
const report = {
|
|||
|
|
hypothesis: 'H4 · sensitive-paths 新规则兼容性',
|
|||
|
|
platform: os.platform() + ' ' + os.release(),
|
|||
|
|
timestamp: new Date().toISOString(),
|
|||
|
|
rulesCompiledHasNewRules: compiledHas,
|
|||
|
|
truePositives: hitResults,
|
|||
|
|
trueNegatives: missResults,
|
|||
|
|
stats: { total, pass, failed: total - pass },
|
|||
|
|
verdict: {
|
|||
|
|
rulesActive: compiledHas,
|
|||
|
|
precisionOk: missResults.every(r => r.pass),
|
|||
|
|
recallOk: hitResults.every(r => r.pass),
|
|||
|
|
pass: pass === total && compiledHas,
|
|||
|
|
},
|
|||
|
|
recommendation: pass === total && compiledHas
|
|||
|
|
? '✅ 新规则精确匹配, 不误伤, 已进 rules-compiled; 可进入冲刺 3 实施'
|
|||
|
|
: '❌ 有误判, 需调整 regex',
|
|||
|
|
};
|
|||
|
|
fs.writeFileSync(path.join(SANDBOX, 'h4-report.json'), JSON.stringify(report, null, 2), 'utf8');
|
|||
|
|
console.log(JSON.stringify(report, null, 2));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main();
|