#!/usr/bin/env node /** * PreToolUse Hook: Edit/Write 预检派遣器 (v6.2 M2) * Matcher: Write|Edit * * 合并 2 个 PreToolUse 钩子为单进程,减少 ~100ms 串行开销: * 1. block-sensitive-files (fail-close, 安全优先) * 2. constitution-precheck (fail-close, ERROR 级规则) * * 读取 stdin 一次,依次调用两个子模块的导出函数。 * 退出码: 0=放行 | 2=阻断(deny/ask) */ 'use strict'; const path = require('path'); const readStdin = require('./lib/read-stdin.js'); function main() { readStdin({ maxSize: 1024 * 1024 }).then(input => { // --- 阶段 1: block-sensitive-files (安全优先, fail-close) --- // 优先加载 block-sensitive-files.js; 若 disabled (.bak) 则尝试 .bak; 都不存在则跳过 try { let bsf = null; try { bsf = require('./block-sensitive-files.js'); } catch {} if (!bsf || !bsf.checkFile) { try { bsf = require('./block-sensitive-files.js.bak'); } catch {} } if (bsf && bsf.checkFile) { const result = bsf.checkFile(input); if (result) { process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: result.decision }, systemMessage: result.message, })); process.exit(2); return; } } // 模块不存在或无 checkFile 导出 → 视为 disabled,跳过 } catch (e) { // block-sensitive-files 加载失败 → fail-close: ask process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: 'ask' }, systemMessage: `[安全防护] block-sensitive-files 模块加载失败(${e.message}),请用户确认是否继续。`, })); process.exit(2); return; } // --- 阶段 2: constitution-precheck (ERROR 级违规拦截) --- try { const { isEnabled } = require('../scripts/feature-flags.js'); if (!isEnabled('constitution-precheck')) { process.exit(0); return; } } catch {} try { const cp = require('./constitution-precheck.js'); if (cp && cp.detectViolation && cp.extractContent && cp.CODE_EXTENSIONS) { const fp = input.tool_input?.file_path || ''; if (fp && cp.CODE_EXTENSIONS.test(fp)) { const content = cp.extractContent(input.tool_input); if (content) { const v = cp.detectViolation(content); if (v) { // 记录安全事件 try { const { logSecurityEvent } = require('./lib/security-log.js'); logSecurityEvent('deny', 'constitution-precheck', v.id, fp); } catch {} process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: 'deny' }, systemMessage: [ '[constitution-precheck] ERROR 级违规,已阻断写入', `文件: ${path.basename(fp)}, 行: ${v.line}`, `规则: [${v.id}] ${v.label}`, '请修复后重试。参考: constitution/AI-CONSTITUTION.md 第十一章', ].join('\n'), })); process.exit(2); return; } } } } } catch {} // 全部通过,放行 process.exit(0); }).catch((e) => { // Fail-close: stdin 解析异常时 ask process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: 'ask' }, systemMessage: `[edit-precheck-dispatcher] 预检异常(${e.message}),请用户确认是否继续。`, })); process.exit(2); }); } if (typeof module !== 'undefined') { module.exports = { main }; } if (require.main === module) { main(); }