mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 17:59:43 +08:00
chore: remove legacy insaits integration
This commit is contained in:
parent
e09c548edf
commit
8488811b80
@ -155,6 +155,7 @@ Keep this file detailed for only the current sprint, blockers, and next actions.
|
|||||||
- 2026-04-05: Direct-ported the safe `globals` bump from PR `#1243` into `main` as part of the council lane and closed the PR as superseded.
|
- 2026-04-05: Direct-ported the safe `globals` bump from PR `#1243` into `main` as part of the council lane and closed the PR as superseded.
|
||||||
- 2026-04-05: Closed PR `#1232` after full audit. The proposed `skill-scout` workflow overlaps current `search-first`, `/skill-create`, and `skill-stocktake`; if a dedicated marketplace-discovery layer returns later it should be rebuilt on top of the current install/catalog model rather than landing as a parallel discovery path.
|
- 2026-04-05: Closed PR `#1232` after full audit. The proposed `skill-scout` workflow overlaps current `search-first`, `/skill-create`, and `skill-stocktake`; if a dedicated marketplace-discovery layer returns later it should be rebuilt on top of the current install/catalog model rather than landing as a parallel discovery path.
|
||||||
- 2026-04-05: Ported the safe localized README switcher fixes from PR `#1209` directly into `main` rather than merging the docs PR wholesale. The navigation now consistently includes `Português (Brasil)` and `Türkçe` across the localized README switchers, while newer localized body copy stays intact.
|
- 2026-04-05: Ported the safe localized README switcher fixes from PR `#1209` directly into `main` rather than merging the docs PR wholesale. The navigation now consistently includes `Português (Brasil)` and `Türkçe` across the localized README switchers, while newer localized body copy stays intact.
|
||||||
|
- 2026-04-05: Removed the stale InsAIts shipped surface from `main`. ECC no longer ships the external Python MCP entry, opt-in hook wiring, wrapper/monitor scripts, or current docs mentions for `insa-its`; changelog history remains, but the live product surface is now fully ECC-native again.
|
||||||
- 2026-04-05: Salvaged the reusable Hermes-generated operator workflow lane without replaying the whole branch. Added six ECC-native top-level skills instead of the old nested `skills/hermes-generated/*` tree: `automation-audit-ops`, `email-ops`, `finance-billing-ops`, `messages-ops`, `research-ops`, and `terminal-ops`. `research-ops` now wraps the existing research stack, while the other five extend `operator-workflows` without introducing any external runtime assumptions.
|
- 2026-04-05: Salvaged the reusable Hermes-generated operator workflow lane without replaying the whole branch. Added six ECC-native top-level skills instead of the old nested `skills/hermes-generated/*` tree: `automation-audit-ops`, `email-ops`, `finance-billing-ops`, `messages-ops`, `research-ops`, and `terminal-ops`. `research-ops` now wraps the existing research stack, while the other five extend `operator-workflows` without introducing any external runtime assumptions.
|
||||||
- 2026-04-05: Added `skills/product-capability` plus `docs/examples/product-capability-template.md` as the canonical PRD-to-SRS lane for issue `#1185`. This is the ECC-native capability-contract step between vague product intent and implementation, and it lives in `business-content` rather than spawning a parallel planning subsystem.
|
- 2026-04-05: Added `skills/product-capability` plus `docs/examples/product-capability-template.md` as the canonical PRD-to-SRS lane for issue `#1185`. This is the ECC-native capability-contract step between vague product intent and implementation, and it lives in `business-content` rather than spawning a parallel planning subsystem.
|
||||||
- 2026-04-05: Tightened `product-lens` so it no longer overlaps the new capability-contract lane. `product-lens` now explicitly owns product diagnosis / brief validation, while `product-capability` owns implementation-ready capability plans and SRS-style constraints.
|
- 2026-04-05: Tightened `product-lens` so it no longer overlaps the new capability-contract lane. `product-lens` now explicitly owns product diagnosis / brief validation, while `product-capability` owns implementation-ready capability plans and SRS-style constraints.
|
||||||
|
|||||||
@ -89,7 +89,7 @@ Este repositório contém apenas o código. Os guias explicam tudo.
|
|||||||
- **Revisão de orquestração** — Pontuação de auditoria de harness tornado determinístico, status de orquestração e compatibilidade de launcher reforçados, prevenção de loop de observer com guarda de 5 camadas.
|
- **Revisão de orquestração** — Pontuação de auditoria de harness tornado determinístico, status de orquestração e compatibilidade de launcher reforçados, prevenção de loop de observer com guarda de 5 camadas.
|
||||||
- **Confiabilidade do observer** — Correção de explosão de memória com throttling e tail sampling, correção de acesso sandbox, lógica de início preguiçoso e guarda de reentrância.
|
- **Confiabilidade do observer** — Correção de explosão de memória com throttling e tail sampling, correção de acesso sandbox, lógica de início preguiçoso e guarda de reentrância.
|
||||||
- **12 ecossistemas de linguagem** — Novas regras para Java, PHP, Perl, Kotlin/Android/KMP, C++ e Rust se juntam ao TypeScript, Python, Go e regras comuns existentes.
|
- **12 ecossistemas de linguagem** — Novas regras para Java, PHP, Perl, Kotlin/Android/KMP, C++ e Rust se juntam ao TypeScript, Python, Go e regras comuns existentes.
|
||||||
- **Contribuições da comunidade** — Traduções para coreano e chinês, hook de segurança InsAIts, otimização de hook biome, skills VideoDB, skills operacionais Evos, instalador PowerShell, suporte ao IDE Antigravity.
|
- **Contribuições da comunidade** — Traduções para coreano e chinês, otimização de hook biome, skills VideoDB, skills operacionais Evos, instalador PowerShell, suporte ao IDE Antigravity.
|
||||||
- **CI reforçado** — 19 correções de falhas de teste, aplicação de contagem de catálogo, validação de manifesto de instalação e suíte de testes completa no verde.
|
- **CI reforçado** — 19 correções de falhas de teste, aplicação de contagem de catálogo, validação de manifesto de instalação e suíte de testes completa no verde.
|
||||||
|
|
||||||
### v1.8.0 — Sistema de Desempenho de Harness (Mar 2026)
|
### v1.8.0 — Sistema de Desempenho de Harness (Mar 2026)
|
||||||
|
|||||||
@ -90,7 +90,7 @@
|
|||||||
* **编排系统大修** — 使治理审核评分具有确定性,强化编排状态和启动器兼容性,通过 5 层防护防止观察者循环。
|
* **编排系统大修** — 使治理审核评分具有确定性,强化编排状态和启动器兼容性,通过 5 层防护防止观察者循环。
|
||||||
* **观察者可靠性** — 通过节流和尾部采样修复内存爆炸问题,修复沙箱访问,实现延迟启动逻辑,并增加重入防护。
|
* **观察者可靠性** — 通过节流和尾部采样修复内存爆炸问题,修复沙箱访问,实现延迟启动逻辑,并增加重入防护。
|
||||||
* **12 个语言生态系统** — 新增 Java、PHP、Perl、Kotlin/Android/KMP、C++ 和 Rust 规则,与现有的 TypeScript、Python、Go 及通用规则并列。
|
* **12 个语言生态系统** — 新增 Java、PHP、Perl、Kotlin/Android/KMP、C++ 和 Rust 规则,与现有的 TypeScript、Python、Go 及通用规则并列。
|
||||||
* **社区贡献** — 韩语和中文翻译,InsAIts 安全钩子,biome 钩子优化,VideoDB 技能,Evos 操作技能,PowerShell 安装程序,Antigravity IDE 支持。
|
* **社区贡献** — 韩语和中文翻译,biome 钩子优化,VideoDB 技能,Evos 操作技能,PowerShell 安装程序,Antigravity IDE 支持。
|
||||||
* **CI 强化** — 修复 19 个测试失败问题,强制执行目录计数,验证安装清单,并使完整测试套件通过。
|
* **CI 强化** — 修复 19 个测试失败问题,强制执行目录计数,验证安装清单,并使完整测试套件通过。
|
||||||
|
|
||||||
### v1.8.0 — 平台性能系统(2026 年 3 月)
|
### v1.8.0 — 平台性能系统(2026 年 3 月)
|
||||||
|
|||||||
@ -25,8 +25,6 @@
|
|||||||
| **Git 推送提醒器** | `Bash` | 在 `git push` 前提醒检查变更 | 0 (警告) |
|
| **Git 推送提醒器** | `Bash` | 在 `git push` 前提醒检查变更 | 0 (警告) |
|
||||||
| **文档文件警告器** | `Write` | 对非标准 `.md`/`.txt` 文件发出警告(允许 README、CLAUDE、CONTRIBUTING、CHANGELOG、LICENSE、SKILL、docs/、skills/);跨平台路径处理 | 0 (警告) |
|
| **文档文件警告器** | `Write` | 对非标准 `.md`/`.txt` 文件发出警告(允许 README、CLAUDE、CONTRIBUTING、CHANGELOG、LICENSE、SKILL、docs/、skills/);跨平台路径处理 | 0 (警告) |
|
||||||
| **策略性压缩提醒器** | `Edit\|Write` | 建议在逻辑间隔(约每 50 次工具调用)手动执行 `/compact` | 0 (警告) |
|
| **策略性压缩提醒器** | `Edit\|Write` | 建议在逻辑间隔(约每 50 次工具调用)手动执行 `/compact` | 0 (警告) |
|
||||||
| **InsAIts 安全监控器(可选加入)** | `Bash\|Write\|Edit\|MultiEdit` | 对高信号工具输入的可选安全扫描。除非设置 `ECC_ENABLE_INSAITS=1`,否则禁用。对关键发现进行拦截,对非关键发现发出警告,并将审计日志写入 `.insaits_audit_session.jsonl`。需要 `pip install insa-its`。[详情](../../../scripts/hooks/insaits-security-monitor.py) | 2 (拦截关键) / 0 (警告) |
|
|
||||||
|
|
||||||
### PostToolUse 钩子
|
### PostToolUse 钩子
|
||||||
|
|
||||||
| 钩子 | 匹配器 | 功能 |
|
| 钩子 | 匹配器 | 功能 |
|
||||||
|
|||||||
@ -26,8 +26,6 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes
|
|||||||
| **Pre-commit quality check** | `Bash` | Runs quality checks before `git commit`: lints staged files, validates commit message format when provided via `-m/--message`, detects console.log/debugger/secrets | 2 (blocks critical) / 0 (warns) |
|
| **Pre-commit quality check** | `Bash` | Runs quality checks before `git commit`: lints staged files, validates commit message format when provided via `-m/--message`, detects console.log/debugger/secrets | 2 (blocks critical) / 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) |
|
| **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) |
|
| **Strategic compact** | `Edit\|Write` | Suggests manual `/compact` at logical intervals (every ~50 tool calls) | 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
|
### PostToolUse Hooks
|
||||||
|
|
||||||
| Hook | Matcher | What It Does |
|
| Hook | Matcher | What It Does |
|
||||||
|
|||||||
@ -92,18 +92,6 @@
|
|||||||
"description": "Capture tool use observations for continuous learning",
|
"description": "Capture tool use observations for continuous learning",
|
||||||
"id": "pre:observe:continuous-learning"
|
"id": "pre:observe:continuous-learning"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"matcher": "Bash|Write|Edit|MultiEdit",
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"pre:insaits-security\" \"scripts/hooks/insaits-security-wrapper.js\" \"standard,strict\"",
|
|
||||||
"timeout": 15
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Optional InsAIts AI security monitor for Bash/Edit/Write flows. Enable with ECC_ENABLE_INSAITS=1. Requires: pip install insa-its",
|
|
||||||
"id": "pre:insaits:security"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"matcher": "Bash|Write|Edit|MultiEdit",
|
"matcher": "Bash|Write|Edit|MultiEdit",
|
||||||
"hooks": [
|
"hooks": [
|
||||||
|
|||||||
@ -104,11 +104,6 @@
|
|||||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/your/projects"],
|
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/your/projects"],
|
||||||
"description": "Filesystem operations (set your path)"
|
"description": "Filesystem operations (set your path)"
|
||||||
},
|
},
|
||||||
"insaits": {
|
|
||||||
"command": "python3",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"playwright": {
|
"playwright": {
|
||||||
"command": "npx",
|
"command": "npx",
|
||||||
"args": ["-y", "@playwright/mcp", "--browser", "chrome"],
|
"args": ["-y", "@playwright/mcp", "--browser", "chrome"],
|
||||||
|
|||||||
@ -1,269 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
InsAIts Security Monitor -- PreToolUse Hook for Claude Code
|
|
||||||
============================================================
|
|
||||||
|
|
||||||
Real-time security monitoring for Claude Code tool inputs.
|
|
||||||
Detects 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
|
|
||||||
export ECC_ENABLE_INSAITS=1
|
|
||||||
|
|
||||||
Add to .claude/settings.json:
|
|
||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"PreToolUse": [
|
|
||||||
{
|
|
||||||
"matcher": "Bash|Write|Edit|MultiEdit",
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "node scripts/hooks/insaits-security-wrapper.js"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
How it works:
|
|
||||||
Claude Code passes tool input as JSON on stdin.
|
|
||||||
This script runs InsAIts anomaly detection on the content.
|
|
||||||
Exit code 0 = clean (pass through).
|
|
||||||
Exit code 2 = critical issue found (blocks tool execution).
|
|
||||||
Stderr output = non-blocking warning shown to Claude.
|
|
||||||
|
|
||||||
Environment variables:
|
|
||||||
INSAITS_DEV_MODE Set to "true" to enable dev mode (no API key needed).
|
|
||||||
Defaults to "false" (strict mode).
|
|
||||||
INSAITS_MODEL LLM model identifier for fingerprinting. Default: claude-opus.
|
|
||||||
INSAITS_FAIL_MODE "open" (default) = continue on SDK errors.
|
|
||||||
"closed" = block tool execution on SDK errors.
|
|
||||||
INSAITS_VERBOSE Set to any value to enable debug logging.
|
|
||||||
|
|
||||||
Detections include:
|
|
||||||
- Credential exposure (API keys, tokens, passwords)
|
|
||||||
- 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
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from typing import Any, Dict, List, Tuple
|
|
||||||
|
|
||||||
# Configure logging to stderr so it does not interfere with stdout protocol
|
|
||||||
logging.basicConfig(
|
|
||||||
stream=sys.stderr,
|
|
||||||
format="[InsAIts] %(message)s",
|
|
||||||
level=logging.DEBUG if os.environ.get("INSAITS_VERBOSE") else logging.WARNING,
|
|
||||||
)
|
|
||||||
log = logging.getLogger("insaits-hook")
|
|
||||||
|
|
||||||
# Try importing InsAIts SDK
|
|
||||||
try:
|
|
||||||
from insa_its import insAItsMonitor
|
|
||||||
INSAITS_AVAILABLE: bool = True
|
|
||||||
except ImportError:
|
|
||||||
INSAITS_AVAILABLE = False
|
|
||||||
|
|
||||||
# --- Constants ---
|
|
||||||
AUDIT_FILE: str = ".insaits_audit_session.jsonl"
|
|
||||||
MIN_CONTENT_LENGTH: int = 10
|
|
||||||
MAX_SCAN_LENGTH: int = 4000
|
|
||||||
DEFAULT_MODEL: str = "claude-opus"
|
|
||||||
BLOCKING_SEVERITIES: frozenset = frozenset({"CRITICAL"})
|
|
||||||
|
|
||||||
|
|
||||||
def extract_content(data: Dict[str, Any]) -> Tuple[str, str]:
|
|
||||||
"""Extract inspectable text from a Claude Code tool input payload.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A (text, context) tuple where *text* is the content to scan and
|
|
||||||
*context* is a short label for the audit log.
|
|
||||||
"""
|
|
||||||
tool_name: str = data.get("tool_name", "")
|
|
||||||
tool_input: Dict[str, Any] = data.get("tool_input", {})
|
|
||||||
|
|
||||||
text: str = ""
|
|
||||||
context: str = ""
|
|
||||||
|
|
||||||
if tool_name in ("Write", "Edit", "MultiEdit"):
|
|
||||||
text = tool_input.get("content", "") or tool_input.get("new_string", "")
|
|
||||||
context = "file:" + str(tool_input.get("file_path", ""))[:80]
|
|
||||||
elif tool_name == "Bash":
|
|
||||||
# PreToolUse: the tool hasn't executed yet, inspect the command
|
|
||||||
command: str = str(tool_input.get("command", ""))
|
|
||||||
text = command
|
|
||||||
context = "bash:" + command[:80]
|
|
||||||
elif "content" in data:
|
|
||||||
content: Any = 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 = str(data.get("task", ""))
|
|
||||||
|
|
||||||
return text, context
|
|
||||||
|
|
||||||
|
|
||||||
def write_audit(event: Dict[str, Any]) -> None:
|
|
||||||
"""Append an audit event to the JSONL audit log.
|
|
||||||
|
|
||||||
Creates a new dict to avoid mutating the caller's *event*.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
enriched: Dict[str, Any] = {
|
|
||||||
**event,
|
|
||||||
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
||||||
}
|
|
||||||
enriched["hash"] = hashlib.sha256(
|
|
||||||
json.dumps(enriched, sort_keys=True).encode()
|
|
||||||
).hexdigest()[:16]
|
|
||||||
with open(AUDIT_FILE, "a", encoding="utf-8") as f:
|
|
||||||
f.write(json.dumps(enriched) + "\n")
|
|
||||||
except OSError as exc:
|
|
||||||
log.warning("Failed to write audit log %s: %s", AUDIT_FILE, exc)
|
|
||||||
|
|
||||||
|
|
||||||
def get_anomaly_attr(anomaly: Any, key: str, default: str = "") -> str:
|
|
||||||
"""Get a field from an anomaly that may be a dict or an object.
|
|
||||||
|
|
||||||
The SDK's ``send_message()`` returns anomalies as dicts, while
|
|
||||||
other code paths may return dataclass/object instances. This
|
|
||||||
helper handles both transparently.
|
|
||||||
"""
|
|
||||||
if isinstance(anomaly, dict):
|
|
||||||
return str(anomaly.get(key, default))
|
|
||||||
return str(getattr(anomaly, key, default))
|
|
||||||
|
|
||||||
|
|
||||||
def format_feedback(anomalies: List[Any]) -> str:
|
|
||||||
"""Format detected anomalies as feedback for Claude Code.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A human-readable multi-line string describing each finding.
|
|
||||||
"""
|
|
||||||
lines: List[str] = [
|
|
||||||
"== InsAIts Security Monitor -- Issues Detected ==",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
for i, a in enumerate(anomalies, 1):
|
|
||||||
sev: str = get_anomaly_attr(a, "severity", "MEDIUM")
|
|
||||||
atype: str = get_anomaly_attr(a, "type", "UNKNOWN")
|
|
||||||
detail: str = get_anomaly_attr(a, "details", "")
|
|
||||||
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() -> None:
|
|
||||||
"""Entry point for the Claude Code PreToolUse hook."""
|
|
||||||
raw: str = sys.stdin.read().strip()
|
|
||||||
if not raw:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
data: Dict[str, Any] = json.loads(raw)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
data = {"content": raw}
|
|
||||||
|
|
||||||
text, context = extract_content(data)
|
|
||||||
|
|
||||||
# Skip very short content (e.g. "OK", empty bash results)
|
|
||||||
if len(text.strip()) < MIN_CONTENT_LENGTH:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if not INSAITS_AVAILABLE:
|
|
||||||
log.warning("Not installed. Run: pip install insa-its")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Wrap SDK calls so an internal error does not crash the hook
|
|
||||||
try:
|
|
||||||
monitor: insAItsMonitor = insAItsMonitor(
|
|
||||||
session_name="claude-code-hook",
|
|
||||||
dev_mode=os.environ.get(
|
|
||||||
"INSAITS_DEV_MODE", "false"
|
|
||||||
).lower() in ("1", "true", "yes"),
|
|
||||||
)
|
|
||||||
result: Dict[str, Any] = monitor.send_message(
|
|
||||||
text=text[:MAX_SCAN_LENGTH],
|
|
||||||
sender_id="claude-code",
|
|
||||||
llm_id=os.environ.get("INSAITS_MODEL", DEFAULT_MODEL),
|
|
||||||
)
|
|
||||||
except Exception as exc: # Broad catch intentional: unknown SDK internals
|
|
||||||
fail_mode: str = os.environ.get("INSAITS_FAIL_MODE", "open").lower()
|
|
||||||
if fail_mode == "closed":
|
|
||||||
sys.stdout.write(
|
|
||||||
f"InsAIts SDK error ({type(exc).__name__}); "
|
|
||||||
"blocking execution to avoid unscanned input.\n"
|
|
||||||
)
|
|
||||||
sys.exit(2)
|
|
||||||
log.warning(
|
|
||||||
"SDK error (%s), skipping security scan: %s",
|
|
||||||
type(exc).__name__, exc,
|
|
||||||
)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
anomalies: List[Any] = 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": [get_anomaly_attr(a, "type") for a in anomalies],
|
|
||||||
"text_length": len(text),
|
|
||||||
})
|
|
||||||
|
|
||||||
if not anomalies:
|
|
||||||
log.debug("Clean -- no anomalies detected.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Determine maximum severity
|
|
||||||
has_critical: bool = any(
|
|
||||||
get_anomaly_attr(a, "severity").upper() in BLOCKING_SEVERITIES
|
|
||||||
for a in anomalies
|
|
||||||
)
|
|
||||||
|
|
||||||
feedback: str = format_feedback(anomalies)
|
|
||||||
|
|
||||||
if has_critical:
|
|
||||||
# stdout feedback -> Claude Code shows to the model
|
|
||||||
sys.stdout.write(feedback + "\n")
|
|
||||||
sys.exit(2) # PreToolUse exit 2 = block tool execution
|
|
||||||
else:
|
|
||||||
# Non-critical: warn via stderr (non-blocking)
|
|
||||||
log.warning("\n%s", feedback)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
/**
|
|
||||||
* InsAIts Security Monitor — wrapper for run-with-flags compatibility.
|
|
||||||
*
|
|
||||||
* This thin wrapper receives stdin from the hooks infrastructure and
|
|
||||||
* delegates to the Python-based insaits-security-monitor.py script.
|
|
||||||
*
|
|
||||||
* The wrapper exists because run-with-flags.js spawns child scripts
|
|
||||||
* via `node`, so a JS entry point is needed to bridge to Python.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
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 => {
|
|
||||||
if (raw.length < MAX_STDIN) {
|
|
||||||
raw += chunk.substring(0, MAX_STDIN - raw.length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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');
|
|
||||||
|
|
||||||
// Try python3 first (macOS/Linux), fall back to python (Windows)
|
|
||||||
const pythonCandidates = ['python3', 'python'];
|
|
||||||
let result;
|
|
||||||
|
|
||||||
for (const pythonBin of pythonCandidates) {
|
|
||||||
result = spawnSync(pythonBin, [pyScript], {
|
|
||||||
input: raw,
|
|
||||||
encoding: 'utf8',
|
|
||||||
env: process.env,
|
|
||||||
cwd: process.cwd(),
|
|
||||||
timeout: 14000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// ENOENT means binary not found — try next candidate
|
|
||||||
if (result.error && result.error.code === 'ENOENT') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result || (result.error && result.error.code === 'ENOENT')) {
|
|
||||||
process.stderr.write('[InsAIts] python3/python not found. Install Python 3.9+ and: pip install insa-its\n');
|
|
||||||
process.stdout.write(raw);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log non-ENOENT spawn errors (timeout, signal kill, etc.) so users
|
|
||||||
// know the security monitor did not run — fail-open with a warning.
|
|
||||||
if (result.error) {
|
|
||||||
process.stderr.write(`[InsAIts] Security monitor failed to run: ${result.error.message}\n`);
|
|
||||||
process.stdout.write(raw);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// result.status is null when the process was killed by a signal or
|
|
||||||
// timed out. Check BEFORE writing stdout to avoid leaking partial
|
|
||||||
// or corrupt monitor output. Pass through original raw input instead.
|
|
||||||
if (!Number.isInteger(result.status)) {
|
|
||||||
const signal = result.signal || 'unknown';
|
|
||||||
process.stderr.write(`[InsAIts] Security monitor killed (signal: ${signal}). Tool execution continues.\n`);
|
|
||||||
process.stdout.write(raw);
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.stdout) process.stdout.write(result.stdout);
|
|
||||||
if (result.stderr) process.stderr.write(result.stderr);
|
|
||||||
|
|
||||||
process.exit(result.status);
|
|
||||||
});
|
|
||||||
@ -527,24 +527,6 @@ async function runTests() {
|
|||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
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
|
// check-console-log.js tests
|
||||||
console.log('\ncheck-console-log.js:');
|
console.log('\ncheck-console-log.js:');
|
||||||
|
|
||||||
@ -2025,20 +2007,6 @@ async function runTests() {
|
|||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
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
|
// plugin.json validation
|
||||||
console.log('\nplugin.json Validation:');
|
console.log('\nplugin.json Validation:');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user