# LLM Security Plugin for Claude Code Check skills, plugins and MCP servers before they run with your agent's permissions — and stop secret leaks, destructive commands and prompt injection while you work. > **Solo-maintained, fork-and-own.** A starting point, not a vendor product. Issues are welcome as signals; pull requests are not accepted. See [GOVERNANCE.md](https://git.fromaitochitta.com/open/repo-standard/src/branch/main/GOVERNANCE.md). *AI-generated: all code produced by Claude Code through dialog-driven development. Every change is human-directed, reviewed, and validated before commit. Per Anthropic Consumer Terms §4, ownership of outputs is assigned to the user; this plugin is licensed MIT.* ![Version](https://img.shields.io/badge/version-8.1.3-blue) ![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple) ![Scanners](https://img.shields.io/badge/scanners-22-cyan) ![License](https://img.shields.io/badge/license-MIT-lightgrey) > [!WARNING] > **Rolling this out in an organization?** Antivirus and EDR products such as Microsoft Defender **can** raise alerts on files in this repository, because it contains detection rules and descriptions of attack techniques. Read [Antivirus and EDR alerts](#antivirus-and-edr-alerts) before you deploy, and report any alert to **security@fromaitochitta.com** so it can be fixed. ## Why A Claude Code extension does not just run code — it can give your agent instructions. A malicious skill can tell Claude to read `~/.ssh`, post your environment variables to a webhook, or rewrite its own configuration, while presenting itself as a harmless "project health dashboard". This is the npm/PyPI supply-chain problem with LLM agency added. What you get: - **Guardrails from the moment you install.** Nine hooks run on every prompt and tool call. Five block: secrets written to files, writes to `.env`/`.ssh`/credentials, destructive shell commands, compromised or typosquatted packages, and prompt injection. Four warn. - **A verdict before you install anything.** `/security scan --deep` clones a repository into a temp directory (OS-sandboxed on macOS and on Linux with `bwrap`), runs 14 deterministic scanners plus LLM analysis, and returns ALLOW / WARNING / BLOCK — without loading the repository into your session. - **A grade for your own setup.** `/security posture` scores your Claude Code configuration across 16 posture categories in seconds; `/security audit` gives an A–F report with the fixes. Findings map to [OWASP LLM Top 10 (2025)](https://genai.owasp.org/llm-top-10/), [OWASP Agentic AI Top 10](https://genai.owasp.org/agentic-ai/), OWASP Skills Top 10, MCP Top 10 and the [AI Agent Traps](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6372438) taxonomy (Google DeepMind). ## Install Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) v2.x+ and Node.js (any recent LTS). ```bash claude plugin marketplace add https://git.fromaitochitta.com/open/ktg-plugin-marketplace.git claude plugin install llm-security@ktg-plugin-marketplace ``` The hooks are active immediately. No configuration is needed. ## First five minutes | Run | You get | |-----|---------| | `/security posture` | Scorecard for this project's Claude Code setup, with the top gaps | | `/security scan --deep` | ALLOW / WARNING / BLOCK for a skill, plugin, MCP server or repository | | `/security plugin-audit ` | Install / Review / Do Not Install for a plugin | | `/security audit` | Full A–F audit with a prioritized action plan | | `/security harden --apply` | Grade A reference config (`settings.json`, `CLAUDE.md`, `.gitignore`), with backup | > [!IMPORTANT] > **Scan a repository before you clone it.** A poisoned `CLAUDE.md` enters the model's context the moment you open a cloned repository — before any hook can run. `/security scan --deep` inspects it from a pre-extracted evidence package instead. ## What runs automatically | Hook | When | Effect | |------|------|--------| | Prompt injection scan | Every prompt | **Blocks** override instructions, spoofed system headers, identity redefinition; decodes obfuscation (Unicode Tag, hex, URL, base64, rot13) first | | Secret detection | Edit, Write | **Blocks** AWS/Azure/GitHub/npm tokens, PEM keys, database URLs, JWTs, webhook URLs — 19 patterns, extendable in policy | | Path guard | Edit, Write | **Blocks** writes to `.env*`, `.ssh/`, `.aws/`, `.gnupg/`, credentials, hook scripts, `settings.json` | | Destructive commands | Bash | **Blocks** `rm -rf /`, pipe-to-shell, fork bombs, `chmod 777`, eval tricks — after undoing shell obfuscation | | Supply chain | Bash | **Blocks** known-compromised packages (incl. known typosquats), critical CVEs, and brand-new packages (<72 h, few releases); **warns** on look-alike names — npm, pip, brew, docker, go, cargo, gem | | Output check | After every tool | Warns on injection in tool output and on MCP tool descriptions that drift over time | | Session guard | After every tool | Warns on the "lethal trifecta": untrusted input + sensitive read + an exfiltration step | | Pre-compact scan | Before compaction | Warns on injection or credentials about to survive into the compacted context | | Update check | Once a day | Tells you when a newer version exists (`LLM_SECURITY_UPDATE_CHECK=off` disables it) | Injection and trifecta modes (`block`/`warn`/`off`) are set per project in `.llm-security/policy.json`; the pre-compact mode via `LLM_SECURITY_PRECOMPACT_MODE`. ## All commands | Command | Purpose | |---------|---------| | `/security` | List the sub-commands | | `/security scan [path\|url]` | Supply-chain gate; `--deep` adds the 14 deterministic scanners | | `/security deep-scan [path]` | The 14 deterministic scanners only, with a synthesized report | | `/security plugin-audit [path\|url]` | Plugin trust assessment | | `/security mcp-audit [--live]` | Audit installed MCP server configs | | `/security mcp-inspect` | Connect to running MCP servers and scan their live tool descriptions | | `/security mcp-baseline-reset` | Reset the MCP drift baseline after a legitimate server upgrade | | `/security ide-scan [target\|url]` | Scan VS Code (and forks) and JetBrains extensions, installed or by URL | | `/security supply-check [path]` | Re-audit installed dependencies against blocklists, OSV.dev and typosquats | | `/security posture` | Quick scorecard, 16 categories | | `/security audit` | Full audit, A–F | | `/security dashboard` | Posture across every project on the machine | | `/security diff [path]` | New / resolved / unchanged findings against a baseline | | `/security watch [path]` | Run `diff` on an interval | | `/security registry` | Skill fingerprint registry | | `/security clean [path]` | Fix what is safe to fix, ask about the rest (`--dry-run` to preview) | | `/security harden [path]` | Generate a Grade A config | | `/security threat-model` | Guided STRIDE × MAESTRO session, 15–30 min | | `/security red-team` | 72 attack scenarios against the hooks | | `/security pre-deploy` | Pre-deployment checklist | Every report command also writes a self-contained HTML version and prints a link to it. Scanner-by-scanner detail: [`docs/scanner-reference.md`](docs/scanner-reference.md). ## For teams and CI - **Policy as code** — `.llm-security/policy.json` travels with the repository: hook modes, thresholds, audit log path. A scanned *foreign* repository's own policy and ignore file are not applied, so it cannot suppress its findings. Known exceptions — a tarball or `git archive` export under your working directory — are listed under 8.1.3 in the changelog. - **Runs without Claude Code** — `node bin/llm-security.mjs scan ` (also `deep-scan`, `posture`, `ide-scan`, `audit-bom`, `benchmark`); no API key needed. It is **not** air-gapped: OSV.dev lookups (package names and versions), `npm audit`/`pip-audit`, and DNS checks on URLs it finds reach the network, with no switch to turn them off. Block egress at the network layer if you need an offline run. - **CI gates** — `--fail-on ` exit codes, `--compact` output, SARIF 2.1.0. Templates for GitHub Actions, Azure DevOps and GitLab CI in [`ci/`](ci/); guide in [`docs/ci-cd-guide.md`](docs/ci-cd-guide.md). - **Evidence** — JSONL audit trail with OWASP tags for your SIEM, CycloneDX 1.6 AI bill of materials, and posture checks mapped to EU AI Act, NIST AI RMF and ISO 42001. ## Antivirus and EDR alerts This is a security tool. It ships regular expressions for known-bad code, knowledge files that explain how injection, exfiltration and persistence attacks work, and an encoded demo of a malicious plugin. Nothing in it is malware, but products that use machine learning and heuristics — Microsoft Defender among them ([next-generation protection](https://learn.microsoft.com/defender-endpoint/next-generation-protection)) — can still flag such text. A plain `SKILL.md` in another security repository has been quarantined as `Trojan:Script/Wacatac.H!ml`. What still sits on disk as readable text, and is therefore the likeliest to be flagged: the regex signature tables in `scanners/commons/` (some name hacking tools as plain words), the attack descriptions in `knowledge/`, and the injection phrases and sample commands the examples feed to the hooks. ### What v8.0 → v8.1.3 did about it Most antivirus work landed in **v8.1.0**; v8.1.1 and v8.1.2 did not touch it, and v8.1.3 removed three runnable base64-to-shell lines whose blobs were too short for the gate, and extended the gate to catch them. (v8.0.0 moved the detection tables, including the malware signatures, into the vendored `scanners/commons/` data that ships with the plugin.) | Change (v8.1.0) | Effect | |-----------------|--------| | Test payloads are built at test time in a temp directory, from split strings or rot13 | No runnable malicious fixture is stored in the repository | | The malicious-plugin demo and the poisoned `CLAUDE.md` example ship as encoded archives | They exist in decoded form only in a temp directory while you run them | | Skill-scanner detection lists moved from an agent file to `knowledge/`; runnable one-liners in `knowledge/*.md` rewritten as descriptions | A quarantined knowledge file does not break the plugin; a quarantined agent file would | | Download-into-shell literals removed from hook source | The hook that blocks these commands no longer contains one | | Gate: [`tests/av-surface.test.mjs`](tests/av-surface.test.mjs) | The suite fails if a runnable payload file, a base64 shell-command blob, hidden Unicode (outside the commons conformance corpus), a known payload tree, or a payload literal in agents/commands/hooks returns to disk | Detection is unchanged: the signature tables and the golden baseline are identical before and after. **Not measured:** no clone or install has been tested against Defender on a Windows machine. "No alerts" is the design goal, not a verified result. ### If you get an alert 1. **Do not exclude the folder.** Microsoft advises against exclusions as a fix: "Every exclusion is a protection gap" ([exclusions overview](https://learn.microsoft.com/defender-endpoint/microsoft-defender-antivirus-exclusions-overview)). 2. **Report it to Microsoft as a false positive.** - *Managed devices (Defender for Endpoint / Defender XDR):* your security team submits the file in the Defender portal under **Submissions → Files**, classified as **Clean (false positive)** ([how](https://learn.microsoft.com/defender-endpoint/admin-submissions-mde)). - *Single machine:* restore it under Windows Security → Protection history ([how](https://learn.microsoft.com/defender-endpoint/restore-quarantined-files-microsoft-defender-antivirus)) and submit it at . 3. **Email security@fromaitochitta.com** with the file path, the detection name, the plugin version (or commit), and the antivirus product and its signature version. The alert is handled as a bug: the goal is a patch release in which the file no longer trips the product, with the gate above extended so it stays that way. There is no SLA (see [`SECURITY.md`](SECURITY.md)). If you only need the scanners and hooks, a sparse checkout keeps knowledge files, tests, examples and docs off disk: ```bash git clone --filter=blob:none --no-checkout https://git.fromaitochitta.com/open/llm-security.git cd llm-security git sparse-checkout set scanners hooks git checkout main ``` The hooks do not need `knowledge`; add it to the `set` line for the typosquat checks, `ide-scan`, the skill registry and `red-team`. This affects your own clone only — not what `claude plugin install` puts in the plugin cache. ## Known limitations Prompt injection cannot be fully prevented with today's model architectures; motivated adaptive attackers defeat every published defense. This plugin raises the cost of an attack with independent layers — it does not guarantee against one. | Limit | What to do | |-------|------------| | A cloned repository's `CLAUDE.md` loads before any hook runs | Scan remotely first: `/security scan --deep` | | Regex cannot catch every novel phrasing of an injection | Add an ML classifier such as [parry-guard](https://github.com/vaporif/parry); they run side by side | | Remote-scan clones run without an OS sandbox on Windows, and on Linux where `bwrap` is missing or blocked (Ubuntu 24.04+ by default) — only a warning is printed | Git hardening still applies; on Windows run Claude Code in WSL2 or Docker, on Ubuntu allow `bwrap` in AppArmor | | RAG pipelines, LLM gateways, SSO/SCIM are outside a plugin's reach | Use platform or infrastructure tooling | | Opus with extended context: subagents do not support it and fail | Run `/model Opus` before the security commands | Design rationale: [`docs/defense-philosophy.md`](docs/defense-philosophy.md). Hardening guide: [`docs/security-hardening-guide.md`](docs/security-hardening-guide.md). ## Project scope A solo open-source project in **stabilization mode** since 2026-05-01: bug and security fixes, Claude Code compatibility, knowledge-base refresh. ## Non-goals Out of scope — fork and own them under MIT if you need them: web dashboards or fleet servers, an inline prompt firewall, real-time IDE scanning, compliance evidence packs, ticketing/chat connectors, hosted ML detectors, SSO/RBAC. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the fork-and-own guide. ## Verify it yourself No CI runs the tests for you — this forge has no Actions runner. From a clean clone: ```bash npm test # the full suite node scanners/scan-orchestrator.mjs . # the plugin scanning itself ``` The self-scan is not clean on purpose: a tool that documents attacks and tests against hostile code trips its own scanners. `.llm-security-ignore` suppresses the expected findings and explains each one; delete it to see everything. The encoded malicious-plugin demo is a realistic attack. LLM agents and deterministic scanners together find 85 issues and return BLOCK 100/100 (see its [assessment](examples/malicious-skill-demo/security-assessment.md)). The commands below run the deterministic half: ```bash DEMO="$(node examples/malicious-skill-demo/materialize.mjs)" # decodes into a temp dir, prints the path node scanners/scan-orchestrator.mjs "$DEMO" rm -rf "$(dirname "$DEMO")" ``` For the full pipeline, run `/security scan --deep` in Claude Code before removing the directory. More runnable demonstrations — injection showcase, trifecta walkthrough, MCP rug pull, supply-chain attack, bash evasion — are in [`examples/`](examples/). ## Upgrading from v7 v8.0.0 removed four environment variables. A removed variable is **silently ignored** — `LLM_SECURITY_INJECTION_MODE=off` no longer turns anything off. Find them before you upgrade: ```bash env | grep '^LLM_SECURITY_\(INJECTION_MODE\|TRIFECTA_MODE\|ESCALATION_WINDOW\|AUDIT_LOG\|DEPRECATION_QUIET\)=' grep -rn 'LLM_SECURITY_\(INJECTION_MODE\|TRIFECTA_MODE\|ESCALATION_WINDOW\|AUDIT_LOG\)' \ ~/.zshenv ~/.bashrc .envrc .github/workflows/ 2>/dev/null ``` Move each one into `.llm-security/policy.json`: | Removed env var | Policy key | Default | |-----------------|------------|---------| | `LLM_SECURITY_INJECTION_MODE` | `injection.mode` | `block` | | `LLM_SECURITY_TRIFECTA_MODE` | `trifecta.mode` | `warn` | | `LLM_SECURITY_ESCALATION_WINDOW` | `trifecta.escalation_window` | `5` | | `LLM_SECURITY_AUDIT_LOG` | `audit.log_path` | unset (off) | `LLM_SECURITY_DEPRECATION_QUIET` is gone with the warning it silenced. Details in [`CHANGELOG.md`](CHANGELOG.md) under 8.0.0. ## Changelog - **8.1.3** (2026-09-23) — Python venvs, `vendor/` folders and Claude Code skills count as foreign; both config directories are checked; letter case no longer decides; `/security watch` as a cron job applies the watched project's own ignore and policy files. - **8.1.2** (2026-09-22) — Installed packages (`node_modules`) and Claude Code's plugin directory count as foreign, so their ignore and policy files are not applied. - **8.1.1** (2026-09-22) — A clone, submodule or worktree *under* your working directory counts as foreign. - **8.1.0** (2026-09-22) — Antivirus surface (see above); a scanned foreign repository's own ignore and policy files are no longer applied. - **8.0.0** (2026-09-18) — Breaking cleanup (see [Upgrading from v7](#upgrading-from-v7)); detection tables built from shared, versioned data. Full history: [`CHANGELOG.md`](CHANGELOG.md). ## License and contact MIT — see [`LICENSE`](LICENSE). Built on published research from OWASP, ToxicSkills, ClawHavoc, MCPTox, Pillar Security, Invariant Labs, GHSL Security Lab, Operant AI and Google DeepMind; sources are cited in `knowledge/`. - **Security vulnerabilities and antivirus alerts:** security@fromaitochitta.com — not a public issue. See [`SECURITY.md`](SECURITY.md). - **Bugs and feature requests:** open an issue in the [marketplace repository](https://git.fromaitochitta.com/open/ktg-plugin-marketplace). - **Pull requests:** not accepted; see [`CONTRIBUTING.md`](CONTRIBUTING.md).