mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-01 06:23:28 +08:00
fix: show correct gateguard hook recovery id
This commit is contained in:
committed by
Affaan Mustafa
parent
7c5452f4fa
commit
bb40978e31
@@ -38,6 +38,8 @@ const READ_HEARTBEAT_MS = 60 * 1000;
|
|||||||
const MAX_CHECKED_ENTRIES = 500;
|
const MAX_CHECKED_ENTRIES = 500;
|
||||||
const MAX_SESSION_KEYS = 50;
|
const MAX_SESSION_KEYS = 50;
|
||||||
const ROUTINE_BASH_SESSION_KEY = '__bash_session__';
|
const ROUTINE_BASH_SESSION_KEY = '__bash_session__';
|
||||||
|
const EDIT_WRITE_HOOK_ID = 'pre:edit-write:gateguard-fact-force';
|
||||||
|
const BASH_HOOK_ID = 'pre:bash:gateguard-fact-force';
|
||||||
const ECC_DISABLE_VALUES = new Set(['0', 'false', 'off', 'disabled', 'disable']);
|
const ECC_DISABLE_VALUES = new Set(['0', 'false', 'off', 'disabled', 'disable']);
|
||||||
|
|
||||||
const DESTRUCTIVE_BASH = /\b(rm\s+-rf|git\s+reset\s+--hard|git\s+checkout\s+--|git\s+clean\s+-f|drop\s+table|delete\s+from|truncate|git\s+push\s+--force(?!-with-lease)|git\s+commit\s+--amend|dd\s+if=)\b/i;
|
const DESTRUCTIVE_BASH = /\b(rm\s+-rf|git\s+reset\s+--hard|git\s+checkout\s+--|git\s+clean\s+-f|drop\s+table|delete\s+from|truncate|git\s+push\s+--force(?!-with-lease)|git\s+commit\s+--amend|dd\s+if=)\b/i;
|
||||||
@@ -365,11 +367,12 @@ function routineBashMsg() {
|
|||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function withRecoveryHint(message) {
|
function withRecoveryHint(message, hookIds = [EDIT_WRITE_HOOK_ID]) {
|
||||||
|
const disableTargets = hookIds.map(hookId => `\`${hookId}\``).join(' or ');
|
||||||
return [
|
return [
|
||||||
message,
|
message,
|
||||||
'',
|
'',
|
||||||
'Recovery: if GateGuard is blocking setup or repair work, run this session with `ECC_GATEGUARD=off` or add `pre:edit-write:gateguard-fact-force` to `ECC_DISABLED_HOOKS`.'
|
`Recovery: if GateGuard is blocking setup or repair work, run this session with \`ECC_GATEGUARD=off\` or add ${disableTargets} to \`ECC_DISABLED_HOOKS\`.`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,12 +380,13 @@ function withRecoveryHint(message) {
|
|||||||
|
|
||||||
function denyResult(reason, options = {}) {
|
function denyResult(reason, options = {}) {
|
||||||
const includeRecoveryHint = options.includeRecoveryHint !== false;
|
const includeRecoveryHint = options.includeRecoveryHint !== false;
|
||||||
|
const hookIds = Array.isArray(options.hookIds) && options.hookIds.length > 0 ? options.hookIds : [EDIT_WRITE_HOOK_ID];
|
||||||
return {
|
return {
|
||||||
stdout: JSON.stringify({
|
stdout: JSON.stringify({
|
||||||
hookSpecificOutput: {
|
hookSpecificOutput: {
|
||||||
hookEventName: 'PreToolUse',
|
hookEventName: 'PreToolUse',
|
||||||
permissionDecision: 'deny',
|
permissionDecision: 'deny',
|
||||||
permissionDecisionReason: includeRecoveryHint ? withRecoveryHint(reason) : reason
|
permissionDecisionReason: includeRecoveryHint ? withRecoveryHint(reason, hookIds) : reason
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
exitCode: 0
|
exitCode: 0
|
||||||
@@ -471,7 +475,7 @@ function run(rawInput) {
|
|||||||
if (!markChecked(ROUTINE_BASH_SESSION_KEY)) {
|
if (!markChecked(ROUTINE_BASH_SESSION_KEY)) {
|
||||||
return allowWithStateWarning();
|
return allowWithStateWarning();
|
||||||
}
|
}
|
||||||
return denyResult(routineBashMsg());
|
return denyResult(routineBashMsg(), { hookIds: [BASH_HOOK_ID] });
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawInput; // allow
|
return rawInput; // allow
|
||||||
|
|||||||
@@ -471,7 +471,25 @@ function runTests() {
|
|||||||
'denial reason should mention the existing hook-id disable control');
|
'denial reason should mention the existing hook-id disable control');
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
// --- Test 14: destructive Bash denials do not advertise the recovery escape hatch ---
|
// --- Test 14: routine Bash denial messages show the Bash hook escape hatch ---
|
||||||
|
clearState();
|
||||||
|
if (test('routine Bash denials include Bash hook disable id', () => {
|
||||||
|
const input = {
|
||||||
|
tool_name: 'Bash',
|
||||||
|
tool_input: { command: 'npm test' }
|
||||||
|
};
|
||||||
|
const result = runBashHook(input);
|
||||||
|
const output = parseOutput(result.stdout);
|
||||||
|
const reason = output.hookSpecificOutput.permissionDecisionReason;
|
||||||
|
|
||||||
|
assert.strictEqual(output.hookSpecificOutput.permissionDecision, 'deny');
|
||||||
|
assert.ok(reason.includes('pre:bash:gateguard-fact-force'),
|
||||||
|
'routine Bash denial should show the Bash hook ID');
|
||||||
|
assert.ok(!reason.includes('pre:edit-write:gateguard-fact-force'),
|
||||||
|
'routine Bash denial should not show the Edit/Write hook ID as the targeted disable');
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
// --- Test 15: destructive Bash denials do not advertise the recovery escape hatch ---
|
||||||
clearState();
|
clearState();
|
||||||
if (test('destructive Bash denials omit recovery escape hatch', () => {
|
if (test('destructive Bash denials omit recovery escape hatch', () => {
|
||||||
const input = {
|
const input = {
|
||||||
@@ -487,7 +505,7 @@ function runTests() {
|
|||||||
'destructive gate should not advertise disabling GateGuard');
|
'destructive gate should not advertise disabling GateGuard');
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
// --- Test 15: MultiEdit gates first unchecked file ---
|
// --- Test 16: MultiEdit gates first unchecked file ---
|
||||||
clearState();
|
clearState();
|
||||||
if (test('denies first MultiEdit with unchecked file', () => {
|
if (test('denies first MultiEdit with unchecked file', () => {
|
||||||
const input = {
|
const input = {
|
||||||
|
|||||||
Reference in New Issue
Block a user