claude-injection-guard

March 9, 2026 · View on GitHub

claude-injection-guard

Prompt injection protection for Claude Code agents

Hooks-based, two-stage, local-LLM-powered

CI Python 3.11+ License: Apache 2.0 Code style: ruff Status: Alpha


When Claude Code (or a sub-agent) fetches web content, that content enters the agent's context and can contain malicious instructions designed to hijack its behavior. This is called a prompt injection attack.

claude-injection-guard intercepts fetched content before it reaches Claude, using a fast two-stage pipeline:

  1. Stage 1 — Rule Engine (deterministic, ~1ms): Regex-based pattern matching for known injection signatures. High-confidence hits are blocked immediately; suspicious content escalates to Stage 2.
  2. Stage 2 — Local LLM Guard (~200-800ms, only for escalations): A small local model (Phi-3.5 mini, Qwen-2.5 1.5B, etc.) classifies suspicious content. No data leaves your machine.
[WebFetch result] --> [Stage 1: Rules] --> SAFE --------------------------> Agent
                                        |  SUSPICIOUS
                                  [Stage 2: LLM] --> SAFE ----------------> Agent
                                                  |  INJECTION
                                            BLOCKED + logged + user notified

Table of Contents


Features

FeatureDescription
:hook:Native Claude Code hookZero friction, no proxy, no MCP setup
:zap:Two-stage pipelineLLM only called when rules are uncertain
:house:Sovereign by designLocal LLM, no external classification API
:electric_plug:Backend-agnosticOllama, LM Studio, Docker Model Runner, Apple Silicon MLX
:whale:Optional DockerBring your own Ollama, or use our compose file
:clipboard:Detailed loggingEvery block is logged with reason and matched pattern
:jigsaw:ExtensibleAdd custom patterns in config, no code changes needed

Requirements

  • Python 3.11+
  • Claude Code (CLI)
  • One LLM backend (only needed for Stage 2):
    • Ollama (recommended)
    • LM Studio
    • Docker Desktop with Model Runner
    • mlx-lm (Apple Silicon)

Note: Stage 1 (rule engine) works without any LLM backend. Stage 2 is optional and only called for ambiguous content.


Installation

# 1. Clone the repo
git clone https://github.com/michaelhannecke/claude-injection-guard
cd claude-injection-guard

# 2. Install (minimal -- only pyyaml required)
pip install -e .

# 3. Set up your LLM backend (example: Ollama)
ollama pull phi3.5:mini

# 4. Configure
cp config.example.yml ~/.claude/injection-guard/config.yml
# Edit as needed (defaults work out of the box with Ollama)

Architecture

hooks/post_tool_use.py          ← Claude Code hook entry point (thin wrapper)
guard/
  post_tool_use.py              ← Main hook logic & orchestration
  stage1_rules.py               ← Rule engine: 10 definitive + 15 suspicious patterns
  stage2_llm.py                 ← LLM classifier: 4 backends, structured output
  config.py                     ← YAML config loader, deep_merge, defaults
  logger.py                     ← Structured stderr-only logger
tests/
  test_stage1.py                ← 30 tests
  test_stage2_llm.py            ← 10 tests
  test_post_tool_use.py         ← 17 tests
  test_config.py                ← 6 tests
  test_logger.py                ← 4 tests

Claude Code Hook Setup

Add this to your Claude Code hooks configuration (~/.claude/settings.json or project .claude/settings.json):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "WebFetch|web_fetch",
        "hooks": [
          {
            "type": "command",
            "command": "python3 <path-to>/claude-injection-guard/hooks/post_tool_use.py"
          }
        ]
      }
    ]
  }
}

Note: Replace <path-to> with the absolute path to your local clone, e.g. /Users/you/projects.

See claude_hooks.example.json for a copy-paste template.


Configuration

Copy config.example.yml to ~/.claude/injection-guard/config.yml:

stage2:
  backend: ollama          # ollama | openai_compatible | docker_model_runner | mlx
  model: phi3.5:mini
  endpoint: http://localhost:11434
  confidence_threshold: 0.75

Backend Options

BackendWhen to use
ollamaOllama installed natively (recommended default)
openai_compatibleLM Studio, vLLM, any OpenAI-compatible server
docker_model_runnerDocker Desktop >= 4.40 with Model Runner
mlxApple Silicon, maximum performance, pip install mlx-lm

Optional: Docker Backend

If you prefer not to install Ollama natively:

docker compose up -d
docker compose exec guard-llm ollama pull phi3.5:mini

The hook still runs on the host -- only the LLM inference runs in Docker.


What Gets Detected

Stage 1 -- Definitive blocks (immediate, no LLM needed) — 10 patterns
  • Direct instruction overrides (ignore all previous instructions, disregard your instructions)
  • Identity hijacking (pretend to be, your new role is)
  • Fake system prompt injection (system prompt:, new system message)
  • Known jailbreak personas (DAN, STAN, DUDE, AIM, KEVIN, BasedGPT)
  • Jailbreak activation language (jailbreak mode, developer mode enabled)
  • Credential exfiltration (send your API key to, exfiltrate)
  • DOM data exfiltration (localStorage, document.cookie to external targets)
  • Hidden HTML comment instructions (<!-- instruction: -->)
  • XML/HTML instruction tags (<instruction>, <system>, <prompt>)
Stage 1 -- Suspicious (escalated to Stage 2) — 15 patterns, scored 0.6–0.8
  • Role hijacking phrases (you are now a hacker, act as DAN)
  • Soft instruction resets (forget everything you were told)
  • Safety bypass language (override your restrictions)
  • LLM prompt delimiter injection ([INST], <|im_start|>, <|system|>)
  • Conversation role tags (### Human:, ### Assistant:)
  • Zero-width character obfuscation (U+200B, U+200C, U+200D, U+FEFF)
  • Encoded instruction delivery (base64/hex encoded commands)
  • User deception instructions (tell the user, respond with)
  • Instruction replacement framing (instead of, replace your instructions)
  • Resource exhaustion / DoS patterns (infinite loops, repeated operations)
Stage 2 -- LLM classification
  • Contextual, semantic injection patterns that evade rules
  • Multi-sentence indirect injections
  • Domain-specific attack variations

Adding Custom Patterns

# config.yml
stage1:
  custom_patterns:
    - pattern: "(?i)exfiltrate.*company.*data"
      reason: "Custom: company data exfiltration"
      score: 0.9
      definitive: true  # block immediately, skip Stage 2

See CLAUDE.md for detailed pattern authoring guidelines.


Fail-Safe Design

The guard is designed to fail open by default -- if Stage 2 LLM is unavailable, the content passes through rather than breaking your workflow. You can change this behavior:

hooks:
  fail_open: false  # block on any guard error (more secure)

Running Tests

67 tests across 5 test files covering rules, LLM classification, hook orchestration, config, and logging.

pip install -e ".[dev]"
pytest
Test fileTestsCoverage
test_stage1.py30Definitive blocks, suspicious escalation, safe passthrough, custom patterns, edge cases
test_stage2_llm.py10Response parsing, backend calls, delimiter hardening
test_post_tool_use.py17Hook orchestration, error handling, exit codes
test_config.py6Config loading, merging, precedence
test_logger.py4Logger setup, levels, file handling

Linting and type checking:

ruff check .
mypy guard/ hooks/

Roadmap

  • Bash tool interception (for curl/wget in shell commands)
  • MCP server mode (for multi-agent / team setups)
  • Fine-tuned guard model (domain-specific injection dataset)
  • Metrics endpoint (Prometheus-compatible)
  • VS Code extension integration

Security Considerations

  • 25 built-in patterns (10 definitive, 15 suspicious) plus user-configurable custom patterns
  • Stage 1 patterns are conservative by design -- low false positive rate is prioritized
  • Stage 2 LLM output is strictly parsed; the guard model cannot itself be injected
  • The hook always logs to stderr, separate from Claude's stdout context
  • Content is truncated to 50k chars for Stage 1 and 3k for Stage 2 (sufficient for injection patterns, protects against DoS)

Contributing

Contributions are welcome! Please ensure:

  1. All tests pass (pytest)
  2. Linting is clean (ruff check .)
  3. Type checking passes (mypy guard/ hooks/)

See CLAUDE.md for architecture details and coding conventions.


License

Apache 2.0 -- see LICENSE