From 540f738cc7991466254e054a8a01e0b795d77c50 Mon Sep 17 00:00:00 2001 From: Nomadu27 Date: Tue, 10 Mar 2026 01:02:58 +0100 Subject: [PATCH] feat: add InsAIts PostToolUse security monitoring hook - Add insaits-security-monitor.py: real-time AI security monitoring hook that catches credential exposure, prompt injection, hallucinations, and 20+ other anomaly types - Update hooks.json with InsAIts PostToolUse entry - Update hooks/README.md with InsAIts in PostToolUse table - Add InsAIts MCP server entry to mcp-configs/mcp-servers.json InsAIts (https://github.com/Nomadu27/InsAIts) is an open-source runtime security layer for multi-agent AI. It runs 100% locally and writes tamper-evident audit logs to .insaits_audit_session.jsonl. Install: pip install insa-its Co-Authored-By: Claude Opus 4.6 --- hooks/README.md | 1 + hooks/hooks.json | 11 ++ mcp-configs/mcp-servers.json | 5 + scripts/hooks/insaits-security-monitor.py | 200 ++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 scripts/hooks/insaits-security-monitor.py diff --git a/hooks/README.md b/hooks/README.md index 3b81d6b2..1fc1fb23 100644 --- a/hooks/README.md +++ b/hooks/README.md @@ -36,6 +36,7 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes | **Prettier format** | `Edit` | Auto-formats JS/TS files with Prettier after edits | | **TypeScript check** | `Edit` | Runs `tsc --noEmit` after editing `.ts`/`.tsx` files | | **console.log warning** | `Edit` | Warns about `console.log` statements in edited files | +| **InsAIts security monitor** | `.*` | Real-time AI security: catches credential exposure, prompt injection, hallucinations, behavioral anomalies (23 types). Writes audit log to `.insaits_audit_session.jsonl`. Requires `pip install insa-its`. [Details](../scripts/hooks/insaits-security-monitor.py) | ### Lifecycle Hooks diff --git a/hooks/hooks.json b/hooks/hooks.json index 342b5b81..096a1ac6 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -165,6 +165,17 @@ } ], "description": "Capture tool use results for continuous learning" + }, + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "python \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/insaits-security-monitor.py\"", + "timeout": 15 + } + ], + "description": "InsAIts AI security monitor: catches credential exposure, prompt injection, hallucinations, and 20+ anomaly types. Requires: pip install insa-its" } ], "Stop": [ diff --git a/mcp-configs/mcp-servers.json b/mcp-configs/mcp-servers.json index f6642e31..aa4044e4 100644 --- a/mcp-configs/mcp-servers.json +++ b/mcp-configs/mcp-servers.json @@ -88,6 +88,11 @@ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/your/projects"], "description": "Filesystem operations (set your path)" + }, + "insaits": { + "command": "python", + "args": ["-m", "insa_its.mcp_server"], + "description": "AI-to-AI security monitoring — anomaly detection, credential exposure, hallucination checks, forensic tracing. 23 anomaly types, OWASP MCP Top 10 coverage. 100% local. Install: pip install insa-its" } }, "_comments": { diff --git a/scripts/hooks/insaits-security-monitor.py b/scripts/hooks/insaits-security-monitor.py new file mode 100644 index 00000000..97129e8f --- /dev/null +++ b/scripts/hooks/insaits-security-monitor.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +InsAIts Security Monitor — PostToolUse Hook for Claude Code +============================================================ + +Real-time security monitoring for Claude Code tool outputs. +Catches credential exposure, prompt injection, behavioral anomalies, +hallucination chains, and 20+ other anomaly types — runs 100% locally. + +Writes audit events to .insaits_audit_session.jsonl for forensic tracing. + +Setup: + pip install insa-its + + Add to .claude/settings.json: + { + "hooks": { + "PostToolUse": [ + { + "matcher": ".*", + "hooks": [ + { + "type": "command", + "command": "python scripts/hooks/insaits-security-monitor.py" + } + ] + } + ] + } + } + +How it works: + Claude Code passes tool result as JSON on stdin. + This script runs InsAIts anomaly detection on the output. + Exit code 0 = clean (pass through). + Exit code 2 = critical issue found (blocks action, shows feedback to Claude). + +Detections include: + - Credential exposure (API keys, tokens, passwords in output) + - Prompt injection patterns + - Hallucination indicators (phantom citations, fact contradictions) + - Behavioral anomalies (context loss, semantic drift) + - Tool description divergence + - Shorthand emergence / jargon drift + +All processing is local — no data leaves your machine. + +Author: Cristi Bogdan — YuyAI (https://github.com/Nomadu27/InsAIts) +License: Apache 2.0 +""" + +import sys +import json +import os +import hashlib +import time + +# Try importing InsAIts SDK +try: + from insa_its import insAItsMonitor + INSAITS_AVAILABLE = True +except ImportError: + INSAITS_AVAILABLE = False + +AUDIT_FILE = ".insaits_audit_session.jsonl" + + +def extract_content(data): + """Extract inspectable text from a Claude Code tool result.""" + tool_name = data.get("tool_name", "") + tool_input = data.get("tool_input", {}) + tool_result = data.get("tool_response", {}) + + text = "" + context = "" + + if tool_name in ("Write", "Edit", "MultiEdit"): + text = tool_input.get("content", "") or tool_input.get("new_string", "") + context = "file:" + tool_input.get("file_path", "")[:80] + elif tool_name == "Bash": + if isinstance(tool_result, dict): + text = tool_result.get("output", "") or tool_result.get("stdout", "") + elif isinstance(tool_result, str): + text = tool_result + context = "bash:" + str(tool_input.get("command", ""))[:80] + elif "content" in data: + content = data["content"] + if isinstance(content, list): + text = "\n".join( + b.get("text", "") for b in content if b.get("type") == "text" + ) + elif isinstance(content, str): + text = content + context = data.get("task", "") + + return text, context + + +def write_audit(event): + """Append an audit event to the JSONL audit log.""" + try: + event["timestamp"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + event["hash"] = hashlib.sha256( + json.dumps(event, sort_keys=True).encode() + ).hexdigest()[:16] + with open(AUDIT_FILE, "a", encoding="utf-8") as f: + f.write(json.dumps(event) + "\n") + except OSError: + pass + + +def format_feedback(anomalies): + """Format detected anomalies as feedback for Claude Code.""" + lines = [ + "== InsAIts Security Monitor — Issues Detected ==", + "", + ] + for i, a in enumerate(anomalies, 1): + sev = getattr(a, "severity", "MEDIUM") + atype = getattr(a, "type", "UNKNOWN") + detail = getattr(a, "detail", "") + lines.extend([ + f"{i}. [{sev}] {atype}", + f" {detail[:120]}", + "", + ]) + lines.extend([ + "-" * 56, + "Fix the issues above before continuing.", + "Audit log: " + AUDIT_FILE, + ]) + return "\n".join(lines) + + +def main(): + raw = sys.stdin.read().strip() + if not raw: + sys.exit(0) + + try: + data = json.loads(raw) + except json.JSONDecodeError: + data = {"content": raw} + + text, context = extract_content(data) + + # Skip very short or binary content + if len(text.strip()) < 10: + sys.exit(0) + + if not INSAITS_AVAILABLE: + print( + "[InsAIts] Not installed. Run: pip install insa-its", + file=sys.stderr, + ) + sys.exit(0) + + # Enable dev mode (no API key needed for local detection) + os.environ.setdefault("INSAITS_DEV_MODE", "true") + + monitor = insAItsMonitor(session_name="claude-code-hook") + result = monitor.send_message( + text=text[:4000], + sender_id="claude-code", + llm_id=os.environ.get("INSAITS_MODEL", "claude-opus"), + ) + + anomalies = result.get("anomalies", []) + + # Write audit event regardless of findings + write_audit({ + "tool": data.get("tool_name", "unknown"), + "context": context, + "anomaly_count": len(anomalies), + "anomaly_types": [getattr(a, "type", "") for a in anomalies], + "text_length": len(text), + }) + + if not anomalies: + if os.environ.get("INSAITS_VERBOSE"): + print("[InsAIts] Clean — no anomalies.", file=sys.stderr) + sys.exit(0) + + # Check severity + has_critical = any( + getattr(a, "severity", "") in ("CRITICAL", "critical") for a in anomalies + ) + + feedback = format_feedback(anomalies) + + if has_critical: + print(feedback) # stdout -> Claude Code shows to model + sys.exit(2) # block action + else: + print(feedback, file=sys.stderr) # stderr -> logged only + sys.exit(0) + + +if __name__ == "__main__": + main()