import process from "node:process"; const chunks = []; for await (const chunk of process.stdin) { chunks.push(chunk); } let event; try { event = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch { deny("Raw protection could not parse the tool request."); } const toolName = String( event.tool_name ?? event.toolName ?? event.name ?? "" ).toLowerCase(); const toolInput = event.tool_input ?? event.toolInput ?? event.input ?? {}; const readOnlyTool = /(read|search|grep|list|view|fetch|semantic)/.test(toolName); if (readOnlyTool) { allow(); } const rawTargets = collectPathTargets(toolInput).filter(isRawPath); const command = typeof toolInput.command === "string" ? toolInput.command : ""; const workingDirectory = String(event.cwd ?? ""); const shellTool = /(execute|terminal|shell|bash|powershell|command)/.test(toolName); if (shellTool && (isRawPath(command) || isRawPath(workingDirectory))) { deny( "Shell commands that reference raw/ are blocked. Use a dedicated read or search tool to inspect raw evidence." ); } if (rawTargets.length > 0) { deny(`Modification of raw evidence is forbidden: ${rawTargets[0]}`); } allow(); function collectPathTargets(value, key = "") { if (Array.isArray(value)) { return value.flatMap((item) => collectPathTargets(item, key)); } if (value && typeof value === "object") { return Object.entries(value).flatMap(([childKey, childValue]) => collectPathTargets(childValue, childKey) ); } if (typeof value !== "string") { return []; } if (key === "input") { return [...value.matchAll(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/gm)].map( (match) => match[1].trim() ); } return /(file|path|uri|target|destination|source)/i.test(key) ? [value] : []; } function isRawPath(value) { const normalized = String(value) .replaceAll("\\", "/") .replaceAll('"', "") .replaceAll("'", "") .toLowerCase(); return /(^|[\s/:])raw(?:[\s/]|$)/.test(normalized); } function allow() { process.stdout.write(JSON.stringify({ continue: true })); process.exit(0); } function deny(reason) { process.stdout.write( JSON.stringify({ continue: false, stopReason: reason, hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason } }) ); process.exit(0); }