/** * VerifyCommandPolicy — blocks arbitrary shell commands, only allows package.json scripts. * * Checkpoints: pre-verify-command * * Logic migrated from: * - src/harness/goal-loop.ts: parseGoal, resolveVerifyScript */ import type { GateContext, GateFinding } from "../findings.ts"; export const VERIFY_COMMAND_POLICY_NAME = "verify-command-policy"; export function evaluateVerifyCommandPolicy(ctx: GateContext): GateFinding[] { const findings: GateFinding[] = []; const { command, cwd } = ctx; if (!command) return findings; // Check if command parses as a valid goal const parsed = parseGoal(command); if (!parsed) { findings.push({ id: "VCP-001", severity: "hard-deny", policy: VERIFY_COMMAND_POLICY_NAME, message: `无法解析验证命令:${command}`, nextAction: "请使用 package.json 中已声明的脚本,如:npm run check、npm test。", }); return findings; } // Check if the script exists in package.json const scripts = readPackageScripts(cwd); if (!scripts[parsed.script]) { findings.push({ id: "VCP-002", severity: "hard-deny", policy: VERIFY_COMMAND_POLICY_NAME, message: `脚本 ${parsed.script} 未在 package.json 中声明。`, nextAction: `可用脚本:${Object.keys(scripts).join(", ") || "无"}`, }); } return findings; } // ── Utility functions (reimplemented from src/harness/goal-loop.ts) ────────── interface ParsedGoal { script: string; } function parseGoal(text: string): ParsedGoal | null { const runMatch = text.match(/\bnpm\s+run\s+([A-Za-z0-9:_-]+)\b/i); if (runMatch) { return { script: runMatch[1] }; } if (/\bnpm\s+test\b/i.test(text)) { return { script: "test" }; } return null; } function readPackageScripts(cwd: string): Record { try { const { existsSync, readFileSync } = require("node:fs"); const { join } = require("node:path"); const pkgPath = join(cwd, "package.json"); if (!existsSync(pkgPath)) return {}; const pkg = JSON.parse(readFileSync(pkgPath, "utf8")); return pkg.scripts ?? {}; } catch { return {}; } }