fix: MultiEdit gate bypass — handle edits[].file_path correctly

P1 bug reported by greptile-apps: MultiEdit uses toolInput.edits[].file_path,
not toolInput.file_path. The gate was silently allowing all MultiEdit calls.

Fix: separate MultiEdit into its own branch that iterates edits array
and gates on the first unchecked file_path.

9/9 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
seto
2026-04-13 15:37:39 +09:00
parent 45823fcede
commit 6ed1c643e7

View File

@@ -192,7 +192,7 @@ function run(rawInput) {
const toolName = data.tool_name || '';
const toolInput = data.tool_input || {};
if (toolName === 'Edit' || toolName === 'MultiEdit' || toolName === 'Write') {
if (toolName === 'Edit' || toolName === 'Write') {
const filePath = toolInput.file_path || '';
if (!filePath) {
return rawInput; // allow
@@ -200,14 +200,24 @@ function run(rawInput) {
if (!isChecked(filePath)) {
markChecked(filePath);
const msg = (toolName === 'Edit' || toolName === 'MultiEdit')
? editGateMsg(filePath) : writeGateMsg(filePath);
return denyResult(msg);
return denyResult(toolName === 'Edit' ? editGateMsg(filePath) : writeGateMsg(filePath));
}
return rawInput; // allow
}
if (toolName === 'MultiEdit') {
const edits = toolInput.edits || [];
for (const edit of edits) {
const filePath = edit.file_path || '';
if (filePath && !isChecked(filePath)) {
markChecked(filePath);
return denyResult(editGateMsg(filePath));
}
}
return rawInput; // allow
}
if (toolName === 'Bash') {
const command = toolInput.command || '';