From 9c1e8dd1e41b2d7f004a375f3517458a0b59482b Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Tue, 10 Mar 2026 20:47:09 -0700 Subject: [PATCH] fix: make insaits hook opt-in --- hooks/README.md | 2 +- hooks/hooks.json | 4 +-- scripts/hooks/insaits-security-monitor.py | 5 +-- scripts/hooks/insaits-security-wrapper.js | 9 +++++ tests/hooks/hooks.test.js | 41 +++++++++++++++++++++++ 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/hooks/README.md b/hooks/README.md index dde8a020..490c09ba 100644 --- a/hooks/README.md +++ b/hooks/README.md @@ -25,7 +25,7 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes | **Git push reminder** | `Bash` | Reminds to review changes before `git push` | 0 (warns) | | **Doc file warning** | `Write` | Warns about non-standard `.md`/`.txt` files (allows README, CLAUDE, CONTRIBUTING, CHANGELOG, LICENSE, SKILL, docs/, skills/); cross-platform path handling | 0 (warns) | | **Strategic compact** | `Edit\|Write` | Suggests manual `/compact` at logical intervals (every ~50 tool calls) | 0 (warns) | -| **InsAIts security monitor** | `*` | Detects credential exposure, prompt injection, hallucinations, and behavioral anomalies (23 types) before tool execution. Blocks on critical findings, warns on non-critical. Writes audit log to `.insaits_audit_session.jsonl`. Requires `pip install insa-its`. [Details](../scripts/hooks/insaits-security-monitor.py) | 2 (blocks critical) / 0 (warns) | +| **InsAIts security monitor (opt-in)** | `Bash\|Write\|Edit\|MultiEdit` | Optional security scan for high-signal tool inputs. Disabled unless `ECC_ENABLE_INSAITS=1`. Blocks on critical findings, warns on non-critical, and writes audit log to `.insaits_audit_session.jsonl`. Requires `pip install insa-its`. [Details](../scripts/hooks/insaits-security-monitor.py) | 2 (blocks critical) / 0 (warns) | ### PostToolUse Hooks diff --git a/hooks/hooks.json b/hooks/hooks.json index 64b2fe83..3147db2e 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -65,7 +65,7 @@ "description": "Capture tool use observations for continuous learning" }, { - "matcher": "*", + "matcher": "Bash|Write|Edit|MultiEdit", "hooks": [ { "type": "command", @@ -73,7 +73,7 @@ "timeout": 15 } ], - "description": "InsAIts AI security monitor: detects credential exposure, prompt injection, hallucinations, and 20+ anomaly types before tool execution. Requires: pip install insa-its" + "description": "Optional InsAIts AI security monitor for Bash/Edit/Write flows. Enable with ECC_ENABLE_INSAITS=1. Requires: pip install insa-its" } ], "PreCompact": [ diff --git a/scripts/hooks/insaits-security-monitor.py b/scripts/hooks/insaits-security-monitor.py index 2e3080af..da1bbf24 100644 --- a/scripts/hooks/insaits-security-monitor.py +++ b/scripts/hooks/insaits-security-monitor.py @@ -11,17 +11,18 @@ Writes audit events to .insaits_audit_session.jsonl for forensic tracing. Setup: pip install insa-its + export ECC_ENABLE_INSAITS=1 Add to .claude/settings.json: { "hooks": { "PreToolUse": [ { - "matcher": ".*", + "matcher": "Bash|Write|Edit|MultiEdit", "hooks": [ { "type": "command", - "command": "python3 scripts/hooks/insaits-security-monitor.py" + "command": "node scripts/hooks/insaits-security-wrapper.js" } ] } diff --git a/scripts/hooks/insaits-security-wrapper.js b/scripts/hooks/insaits-security-wrapper.js index 2ae10857..9f3e46d8 100644 --- a/scripts/hooks/insaits-security-wrapper.js +++ b/scripts/hooks/insaits-security-wrapper.js @@ -16,6 +16,10 @@ const { spawnSync } = require('child_process'); const MAX_STDIN = 1024 * 1024; +function isEnabled(value) { + return ['1', 'true', 'yes', 'on'].includes(String(value || '').toLowerCase()); +} + let raw = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', chunk => { @@ -25,6 +29,11 @@ process.stdin.on('data', chunk => { }); process.stdin.on('end', () => { + if (!isEnabled(process.env.ECC_ENABLE_INSAITS)) { + process.stdout.write(raw); + process.exit(0); + } + const scriptDir = __dirname; const pyScript = path.join(scriptDir, 'insaits-security-monitor.py'); diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index 2c873204..be7dd394 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -203,6 +203,24 @@ async function runTests() { } })) passed++; else failed++; + // insaits-security-wrapper.js tests + console.log('\ninsaits-security-wrapper.js:'); + + if (await asyncTest('passes through input unchanged when integration is disabled', async () => { + const stdinData = JSON.stringify({ + tool_name: 'Write', + tool_input: { file_path: 'src/index.ts', content: 'console.log("ok");' } + }); + const result = await runScript( + path.join(scriptsDir, 'insaits-security-wrapper.js'), + stdinData, + { ECC_ENABLE_INSAITS: '' } + ); + assert.strictEqual(result.code, 0, `Exit code should be 0, got ${result.code}`); + assert.strictEqual(result.stdout, stdinData, 'Should pass stdin through unchanged'); + assert.strictEqual(result.stderr, '', 'Should stay silent when integration is disabled'); + })) passed++; else failed++; + // check-console-log.js tests console.log('\ncheck-console-log.js:'); @@ -1237,6 +1255,29 @@ async function runTests() { } })) passed++; else failed++; + if (test('InsAIts hook is opt-in and scoped to high-signal tool inputs', () => { + const hooksPath = path.join(__dirname, '..', '..', 'hooks', 'hooks.json'); + const hooks = JSON.parse(fs.readFileSync(hooksPath, 'utf8')); + const insaitsHook = hooks.hooks.PreToolUse.find(entry => + entry.description && entry.description.includes('InsAIts') + ); + + assert.ok(insaitsHook, 'Should define an InsAIts PreToolUse hook'); + assert.strictEqual( + insaitsHook.matcher, + 'Bash|Write|Edit|MultiEdit', + 'InsAIts hook should avoid matching every tool' + ); + assert.ok( + insaitsHook.description.includes('ECC_ENABLE_INSAITS=1'), + 'InsAIts hook should document explicit opt-in' + ); + assert.ok( + insaitsHook.hooks[0].command.includes('insaits-security-wrapper.js'), + 'InsAIts hook should execute through the JS wrapper' + ); + })) passed++; else failed++; + // plugin.json validation console.log('\nplugin.json Validation:');