Prompt injection scanner CLI - substring, unicode, secrets, and ML detection
Project description
Parry-guard
Prompt injection scanner for Claude Code hooks. Scans tool inputs and outputs for injection attacks, secrets, and data exfiltration attempts.
Early development — this tool is under active development and may have bugs or false positives. Tested on linux/macOS.
Prerequisites
The ML models are gated on HuggingFace. Before installing:
- Create an account at huggingface.co
- Accept the DeBERTa v3 license (required for all modes)
- For
fullmode: also accept the Llama Prompt Guard 2 license (Meta approval required) - Create an access token at huggingface.co/settings/tokens
Usage
Add to ~/.claude/settings.json:
With uvx:
{
"hooks": {
"PreToolUse": [{ "command": "uvx parry-guard hook", "timeout": 1000 }],
"PostToolUse": [{ "command": "uvx parry-guard hook", "timeout": 5000 }],
"UserPromptSubmit": [{ "command": "uvx parry-guard hook", "timeout": 2000 }]
}
}
With rvx:
{
"hooks": {
"PreToolUse": [{ "command": "rvx parry-guard hook", "timeout": 1000 }],
"PostToolUse": [{ "command": "rvx parry-guard hook", "timeout": 5000 }],
"UserPromptSubmit": [{ "command": "rvx parry-guard hook", "timeout": 2000 }]
}
}
With parry-guard on PATH (via Nix, cargo install, or release binary):
{
"hooks": {
"PreToolUse": [{ "command": "parry-guard hook", "timeout": 1000 }],
"PostToolUse": [{ "command": "parry-guard hook", "timeout": 5000 }],
"UserPromptSubmit": [{ "command": "parry-guard hook", "timeout": 2000 }]
}
}
Other installation methods
From source:
# Default (ONNX backend - statically linked, 5-6x faster than Candle)
cargo install --path crates/cli
# Candle backend (pure Rust, no native deps, portable)
cargo install --path crates/cli --no-default-features --features candle
Nix (home-manager)
# flake.nix
{
inputs.parry.url = "github:vaporif/parry";
outputs = { parry, ... }: {
# pass parry to your home-manager config via extraSpecialArgs, overlays, etc.
};
}
# home-manager module
{ inputs, pkgs, config, ... }: {
imports = [ inputs.parry.homeManagerModules.default ];
programs.parry-guard = {
enable = true;
package = inputs.parry.packages.${pkgs.system}.default; # onnx (default)
# package = inputs.parry.packages.${pkgs.system}.candle; # candle (pure Rust, portable, ~5-6x slower)
hfTokenFile = config.sops.secrets.hf-token.path;
ignoreDirs = [ "/home/user/repos/trusted" ];
# askOnNewProject = true; # Ask before monitoring new projects (default: auto-monitor)
# claudeMdThreshold = 0.9; # ML threshold for CLAUDE.md scanning (default 0.9)
# scanMode = "full"; # fast (default) | full | custom
# Custom models (auto-sets scanMode to "custom")
# models = [
# { repo = "ProtectAI/deberta-v3-small-prompt-injection-v2"; }
# { repo = "meta-llama/Llama-Prompt-Guard-2-86M"; threshold = 0.5; }
# ];
};
}
Setup
1. Configure HuggingFace token
One of (first match wins):
export HF_TOKEN="hf_..." # direct value
export HF_TOKEN_PATH="/path/to/token" # file path
# or place token at /run/secrets/hf-token-scan-injection
The daemon auto-starts on first scan, downloads the model on first run, and idles out after 30 minutes.
Note (non-Nix users): The Nix home-manager module wraps the binary with all config baked in via env vars. Without Nix, set env vars in your shell profile (e.g.
HF_TOKEN,PARRY_IGNORE_DIRS,PARRY_SCAN_MODE) — the hook command inherits them. Alternatively, pass flags directly in the hook command:parry-guard --hf-token-path ~/.hf-token --ignore-dirs /home/user/trusted hook. See Config for all options.
Project scanning
By default, parry auto-monitors every new project — scanning is active from the first session with no prompt. To opt out of a specific repo, run parry-guard ignore <path>.
To restore the old ask-first behavior, set PARRY_ASK_ON_NEW_PROJECT=true (or askOnNewProject = true in Nix). See docs/opt-in-flow.md for the full flow.
| Command | Description |
|---|---|
parry-guard monitor [path] |
Enable scanning for a repo |
parry-guard ignore [path] |
Disable scanning for a repo |
parry-guard reset [path] |
Clear state and caches, back to unknown |
parry-guard status [path] |
Show current repo state and findings |
parry-guard repos |
List all known repos and their states |
All commands default to the current directory if path is omitted.
What each hook does
- PreToolUse: 7-layer security — ignored/unknown repo skip, taint enforcement, CLAUDE.md scanning, exfil blocking, destructive operation detection, sensitive path blocking, input content injection scanning (Write/Edit/Bash/MCP tools)
- PostToolUse: Scans tool output for injection/secrets, auto-taints project on detection
- UserPromptSubmit: Audits
.claude/directory for dangerous permissions, injected commands, hook scripts
Daemon & Cache
The daemon keeps ML models in memory and can be run standalone with parry-guard serve --idle-timeout 1800. Hook calls auto-start it if not running.
Scan results are cached in ~/.parry-guard/scan-cache.redb (30-day TTL, ~8ms cache hits vs ~70ms+ inference). Cache is shared across projects and pruned hourly.
Detection Layers
Multi-stage, fail-closed (if unsure, treat as unsafe):
- Unicode — invisible characters (PUA, unassigned codepoints), homoglyphs, RTL overrides
- Substring — Aho-Corasick matching for known injection phrases
- Secrets — 40+ regex patterns for credentials (AWS, GitHub/GitLab, cloud providers, database URIs, private keys, etc.)
- ML Classification — DeBERTa v3 transformer with text chunking (256 chars, 25 overlap) and head+tail strategy for long texts. Configurable threshold (default 0.7).
- Bash Exfiltration — tree-sitter AST analysis for data exfil: network sinks, command substitution, obfuscation (base64, hex, ROT13), DNS tunneling, cloud storage, 60+ sensitive paths, 40+ exfil domains
- Script Exfiltration — same source→sink analysis for script files across 16 languages
Scan modes
| Mode | Models | Latency/chunk | Backend |
|---|---|---|---|
fast (default) |
DeBERTa v3 | ~50-70ms | any |
full |
DeBERTa v3 + Llama Prompt Guard 2 | ~1.5s | candle only |
custom |
User-defined (~/.config/parry-guard/models.toml) |
varies | any |
Use fast for interactive workflows; full for high-security or batch scanning (parry-guard diff --full). The two models cover different blind spots — DeBERTa v3 catches common injection patterns while Llama Prompt Guard 2 is better at subtle, context-dependent attacks (role-play jailbreaks, indirect injections). Running both as an OR ensemble reduces missed attacks at ~20x higher latency per chunk.
Note:
fullmode requires thecandlebackend — Llama Prompt Guard 2 does not ship an ONNX export. Build with--features candle --no-default-featuresto usefullmode.
Config
Global flags
| Flag | Env | Default | Description |
|---|---|---|---|
--threshold |
PARRY_THRESHOLD |
0.7 | ML detection threshold (0.0–1.0) |
--claude-md-threshold |
PARRY_CLAUDE_MD_THRESHOLD |
0.9 | ML threshold for CLAUDE.md scanning (0.0–1.0) |
--scan-mode |
PARRY_SCAN_MODE |
fast | ML scan mode: fast, full, custom |
--hf-token |
HF_TOKEN |
— | HuggingFace token (direct value) |
--hf-token-path |
HF_TOKEN_PATH |
/run/secrets/hf-token-scan-injection |
HuggingFace token file |
--ask-on-new-project |
PARRY_ASK_ON_NEW_PROJECT |
false | Ask before monitoring new projects (default: auto-monitor) |
--ignore-dirs |
PARRY_IGNORE_DIRS |
— | Parent directories to ignore — all repos under these paths are skipped (comma-separated) |
Subcommand flags
| Flag | Env | Default | Description |
|---|---|---|---|
serve --idle-timeout |
PARRY_IDLE_TIMEOUT |
1800 | Daemon idle timeout in seconds |
diff --full |
— | false | Use ML scan instead of fast-only |
diff -e, --extensions |
— | — | Filter by file extension (comma-separated) |
Env-only
| Env | Default | Description |
|---|---|---|
PARRY_LOG |
warn | Tracing filter (trace, debug, info, warn, error) |
PARRY_LOG_FILE |
~/.parry-guard/parry-guard.log |
Override log file path |
Custom patterns: ~/.config/parry-guard/patterns.toml (add/remove sensitive paths, exfil domains, secret patterns).
Custom models: ~/.config/parry-guard/models.toml (used with --scan-mode custom, see examples/models.toml).
ML Backends
One backend is always required (enforced at compile time). Nix default is ONNX (x86_64-linux, aarch64-linux, aarch64-darwin). Use candle package on other platforms.
| Feature | Description |
|---|---|
onnx-fetch |
ONNX, statically linked (downloads ORT at build time). Default. |
candle |
Pure Rust ML. Portable, no native deps. ~5-6x slower. |
onnx |
ONNX, you provide ORT_DYLIB_PATH. |
onnx-coreml |
(experimental) ONNX with CoreML on Apple Silicon. |
# Build with Candle instead of ONNX
cargo build --no-default-features --features candle
Performance
Apple Silicon, release build, fast mode (DeBERTa v3 only). Candle is 5-6x slower than ONNX (default). Run just bench-candle / just bench-onnx to reproduce (requires HF_TOKEN).
| Scenario | ONNX (default) | Candle |
|---|---|---|
| Short text (1 chunk) | ~10ms | ~61ms |
| Medium text (2 chunks) | ~32ms | ~160ms |
| Long text (6 chunks) | ~136ms | ~683ms |
| Cold start (daemon + model load) | ~580ms | ~1s |
| Fast-scan short-circuit | ~7ms | ~7ms |
| Cached result | ~8ms | ~8ms |
Llama Prompt Guard 2 does not ship an ONNX export, so
fullmode requires thecandlebackend.
Contributing
See CONTRIBUTING.md for development setup, commands, and contribution guidelines.
Credits
- ML model: ProtectAI/deberta-v3-small-prompt-injection-v2
- Same model used by LLM Guard
- Exfil patterns: Inspired by GuardDog (Datadog's malicious package scanner)
- Full scan mode optionally uses Llama Prompt Guard 2 86M by Meta, licensed under the Llama 4 Community License. Built with Llama.
License
MIT
Llama Prompt Guard 2 (used in full scan mode) is licensed separately under the Llama 4 Community License. See LICENSE-LLAMA.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file parry_guard-0.1.0a4-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: parry_guard-0.1.0a4-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 8.8 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2d67d03214a3befd8b2b6a268f8d2ffc097981053f2ea38ecf8415b9ea30572
|
|
| MD5 |
54d3f1a4dcb715fcb76f4b7102c5dbf1
|
|
| BLAKE2b-256 |
2aa0148a6c571ab0c97351c8382fde5ec0dd27122118e3213d3e86cfe770d4cb
|
Provenance
The following attestation bundles were made for parry_guard-0.1.0a4-py3-none-musllinux_1_2_x86_64.whl:
Publisher:
release.yaml on vaporif/parry-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parry_guard-0.1.0a4-py3-none-musllinux_1_2_x86_64.whl -
Subject digest:
e2d67d03214a3befd8b2b6a268f8d2ffc097981053f2ea38ecf8415b9ea30572 - Sigstore transparency entry: 1134722403
- Sigstore integration time:
-
Permalink:
vaporif/parry-guard@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Branch / Tag:
refs/tags/v0.1.0-alpha.4 - Owner: https://github.com/vaporif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file parry_guard-0.1.0a4-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: parry_guard-0.1.0a4-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 8.4 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1acb356699c56ea7a1b9609eee9919ca578f3555f60be03ef006e6f7b1810c6c
|
|
| MD5 |
cc124c55e112b18fdbd4edd766fd9d6e
|
|
| BLAKE2b-256 |
aa8ae8bce7307b7a90bef26c3b77285144b7a3ca0b9ea4b01359290e9a256e44
|
Provenance
The following attestation bundles were made for parry_guard-0.1.0a4-py3-none-musllinux_1_2_aarch64.whl:
Publisher:
release.yaml on vaporif/parry-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parry_guard-0.1.0a4-py3-none-musllinux_1_2_aarch64.whl -
Subject digest:
1acb356699c56ea7a1b9609eee9919ca578f3555f60be03ef006e6f7b1810c6c - Sigstore transparency entry: 1134722430
- Sigstore integration time:
-
Permalink:
vaporif/parry-guard@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Branch / Tag:
refs/tags/v0.1.0-alpha.4 - Owner: https://github.com/vaporif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file parry_guard-0.1.0a4-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: parry_guard-0.1.0a4-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 8.2 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f086ec49748b5ff09cac13eee50d2adeac1255f0d6e6d7f4e5c9f9709dd7d01
|
|
| MD5 |
a4c2c7cbf952b60df6b5a3e6efedf5e9
|
|
| BLAKE2b-256 |
75447ea3751bd56495be36b1c0f68b3dbdd3ce8b1a3ae9a7edd39885dc234aaf
|
Provenance
The following attestation bundles were made for parry_guard-0.1.0a4-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yaml on vaporif/parry-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parry_guard-0.1.0a4-py3-none-macosx_11_0_arm64.whl -
Subject digest:
0f086ec49748b5ff09cac13eee50d2adeac1255f0d6e6d7f4e5c9f9709dd7d01 - Sigstore transparency entry: 1134722451
- Sigstore integration time:
-
Permalink:
vaporif/parry-guard@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Branch / Tag:
refs/tags/v0.1.0-alpha.4 - Owner: https://github.com/vaporif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file parry_guard-0.1.0a4-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: parry_guard-0.1.0a4-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 8.3 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
083d7688901e059becd8df8a10b61aeb8815a6b20537f51aba2e0a3e1764ea21
|
|
| MD5 |
74e4ac4e31b750719a0c0ed5a849c777
|
|
| BLAKE2b-256 |
b6801e03c728f5460bda8649a6ec8f0eab6e9cbd00b5cebabb8212689cbe6b96
|
Provenance
The following attestation bundles were made for parry_guard-0.1.0a4-py3-none-macosx_10_12_x86_64.whl:
Publisher:
release.yaml on vaporif/parry-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
parry_guard-0.1.0a4-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
083d7688901e059becd8df8a10b61aeb8815a6b20537f51aba2e0a3e1764ea21 - Sigstore transparency entry: 1134722376
- Sigstore integration time:
-
Permalink:
vaporif/parry-guard@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Branch / Tag:
refs/tags/v0.1.0-alpha.4 - Owner: https://github.com/vaporif
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@532843dbfe38cf43c17da3f2a640d2c54c531999 -
Trigger Event:
push
-
Statement type: