fix: add gateguard recovery escape hatch

This commit is contained in:
Affaan Mustafa
2026-04-30 10:45:43 -04:00
committed by Affaan Mustafa
parent 4c8499d509
commit cfe770a735
4 changed files with 85 additions and 2 deletions

View File

@@ -408,7 +408,56 @@ function runTests() {
}
})) passed++; else failed++;
// --- Test 10: MultiEdit gates first unchecked file ---
// --- Test 10: respects direct GateGuard env disable for recovery sessions ---
clearState();
if (test('respects ECC_GATEGUARD=off without writing gate state', () => {
const input = {
tool_name: 'Write',
tool_input: { file_path: '/src/env-disabled.js', content: 'export const ok = true;' }
};
const result = runHook(input, { ECC_GATEGUARD: 'off' });
const output = parseOutput(result.stdout);
assert.ok(output, 'should produce valid JSON output');
assert.strictEqual(output.tool_name, 'Write', 'disabled gate should pass through raw input');
assert.ok(!output.hookSpecificOutput, 'disabled gate should not deny the operation');
assert.ok(!fs.existsSync(stateFile), 'disabled gate should not create or mutate gate state');
})) passed++; else failed++;
// --- Test 11: respects legacy GATEGUARD_DISABLED env disable ---
clearState();
if (test('respects GATEGUARD_DISABLED=1 for Bash recovery', () => {
const input = {
tool_name: 'Bash',
tool_input: { command: 'npm test' }
};
const result = runBashHook(input, { GATEGUARD_DISABLED: '1' });
const output = parseOutput(result.stdout);
assert.ok(output, 'should produce valid JSON output');
assert.strictEqual(output.tool_name, 'Bash', 'disabled gate should pass Bash through raw input');
assert.ok(!output.hookSpecificOutput, 'disabled gate should not deny Bash');
assert.ok(!fs.existsSync(stateFile), 'disabled gate should not create or mutate gate state');
})) passed++; else failed++;
// --- Test 12: denial messages show an escape hatch ---
clearState();
if (test('denial messages include direct recovery escape hatch', () => {
const input = {
tool_name: 'Write',
tool_input: { file_path: '/src/recovery-hint.js', content: 'export const ok = true;' }
};
const result = runHook(input);
const output = parseOutput(result.stdout);
assert.strictEqual(output.hookSpecificOutput.permissionDecision, 'deny');
assert.ok(output.hookSpecificOutput.permissionDecisionReason.includes('ECC_GATEGUARD=off'),
'denial reason should show the direct recovery env toggle');
assert.ok(output.hookSpecificOutput.permissionDecisionReason.includes('ECC_DISABLED_HOOKS'),
'denial reason should mention the existing hook-id disable control');
})) passed++; else failed++;
// --- Test 13: MultiEdit gates first unchecked file ---
clearState();
if (test('denies first MultiEdit with unchecked file', () => {
const input = {