This commit is contained in:
Chen, Li
2026-09-24 10:26:02 +08:00
commit 517415c5ba
12 changed files with 630 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
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);
}
+11
View File
@@ -0,0 +1,11 @@
{
"hooks": {
"PreToolUse": [
{
"type": "command",
"command": "node .github/hooks/protect-raw.mjs",
"timeout": 5
}
]
}
}