AI code review agent that detects dangerous patterns in LLM-generated code
Project description
Mastiff
AI code review agent that detects dangerous patterns in LLM-generated code.
Mastiff analyzes git diffs using Claude or OpenAI to detect production-risk patterns across five categories — blocking/deadlocks, race conditions, performance degradation, resource leaks, and security vulnerabilities — scoring each finding by severity and confidence. Works with API keys or directly through the claude / codex CLI (no API key required).
Why Mastiff?
LLM-generated code often looks correct at first glance but can contain subtle patterns that only manifest in production:
- Event loop blocking — synchronous calls in async contexts that freeze the application
- Race conditions — shared mutable state accessed without proper synchronization
- O(n²) algorithms — nested loops and unbounded queries that degrade with scale
- Resource leaks — file handles, connections, and sockets opened but never closed
- Security vulnerabilities — SQL injection, command injection, XSS, hardcoded secrets
Traditional linters catch syntax and style issues. Mastiff focuses specifically on the patterns LLMs tend to introduce — not to replace linters, but to complement them with production-risk awareness.
What It Detects
| Category | Description | Examples |
|---|---|---|
| Blocking/Deadlock | Synchronous blocking calls in async contexts, potential deadlocks | time.sleep() in async, synchronous I/O in event loop, inconsistent lock ordering |
| Race Condition | Shared mutable state without synchronization, TOCTOU | Global variable from multiple threads without locks, non-atomic read-modify-write |
| Degradation | O(n²) algorithms, excessive allocations, unbounded growth | Nested loops, loading entire DB table into memory, missing pagination |
| Resource Leak | Resources opened but not properly closed | open() without context manager, DB connection not returned to pool |
| Security | SQL injection, XSS, command injection, SSRF, hardcoded secrets | String-concatenated SQL queries, os.system() with user input, hardcoded API keys |
Quick Start
pip install mastiff
mastiff init # Creates mastiff.yaml with defaults
mastiff review --staged
Alternative installation methods:
pipx install mastiff
# or
uv tool install mastiff
Mastiff auto-detects which provider to use in this order:
| Priority | Provider | Condition |
|---|---|---|
| 1 | claude-code |
claude CLI on PATH |
| 2 | codex |
codex CLI on PATH |
| 3 | anthropic |
ANTHROPIC_API_KEY set |
| 4 | openai |
OPENAI_API_KEY set (requires mastiff[openai]) |
Note: If you have
claudeorcodexCLI installed, it will be used automatically — no API key needed. To override, setapi.providerinmastiff.yaml.
Setup by provider:
# Claude / Codex CLI (no API key required)
pip install mastiff
# Anthropic API
pip install mastiff
export ANTHROPIC_API_KEY="sk-ant-..."
# OpenAI API
pip install "mastiff[openai]"
export OPENAI_API_KEY="sk-..."
To force a specific provider regardless of auto-detection:
# mastiff.yaml
api:
provider: claude-code # or: codex, anthropic, openai
Output Example
Terminal (default):
Review Findings
┏━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ File ┃ Line ┃ Severity ┃ Category ┃ Title ┃ Confidence ┃
┡━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ api/users.py │ 42 │ critical │ blocking │ time.sleep in async handler │ 92% │
│ db/pool.py │ 15 │ warning │ resource_leak │ Connection not returned │ 78% │
└──────────────┴──────┴──────────┴───────────────┴────────────────────────────┴────────────┘
JSON (--format json):
{
"findings": [
{
"rule_id": "blocking-sync-sleep",
"category": "blocking",
"severity": "critical",
"file_path": "api/users.py",
"line_start": 42,
"title": "time.sleep in async handler",
"confidence": 0.92
}
]
}
Agent (--format agent):
[CRITICAL] src/api/users.py:42 blocking-sync-in-async
time.sleep() blocks the event loop in async handler
FIX: Replace time.sleep(n) with await asyncio.sleep(n)
Usage
CLI
# Review staged changes
mastiff review --staged
# Review a commit range
mastiff review HEAD~3..HEAD
# Choose review depth
mastiff review --staged --profile quick
# JSON output
mastiff review --staged --format json
# Strict mode: exit 2 on any finding
mastiff review --staged --strict
# Agent-friendly output (plain text, no ANSI)
mastiff review --staged --format agent
# Watch mode: continuous monitoring
mastiff watch --profile quick --format agent
Review profiles:
| Profile | Diff budget | Context budget | Use case |
|---|---|---|---|
| quick | 5,000 tokens | 3,000 tokens | Pre-commit, editor saves |
| standard | 20,000 tokens | 15,000 tokens | PR review (default) |
| deep | 50,000 tokens | 30,000 tokens | Release audits |
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Success, no findings |
| 1 | Runtime error (config, API, etc.) |
| 2 | Success, findings present (--strict or --format agent) |
Pre-commit Hook
# Install the pre-commit hook
mastiff install
# Commits are automatically reviewed
git commit -m "feat: add user endpoint"
# → mastiff reviews staged changes
In CI environments (CI=true), the hook runs in strict mode and blocks on any finding. When a baseline exists, only new findings are reported.
LSP Server (Experimental)
mastiff server
Provides real-time diagnostics on file save (quick profile). Configure your editor's LSP client to connect to mastiff.
With Claude Code
Mastiff is designed to review LLM-generated code. When using Claude Code as your development agent, Mastiff acts as an automated safety net that catches production-risk patterns before they reach your codebase.
Automatic feedback loop (recommended):
mastiff install --claude-code
This installs a PostToolUse hook that automatically reviews every file Claude Code writes or edits. When issues are found, they are fed back to Claude Code via stderr, creating an automatic fix loop.
Pre-commit hook:
Install the hook once and every commit Claude Code creates is automatically reviewed:
mastiff install
Claude Code commits through git, so the pre-commit hook runs transparently on every commit. Critical findings block the commit, giving you a chance to review before the code lands.
CI integration:
Add Mastiff to your CI pipeline to review every pull request that Claude Code opens:
# .github/workflows/ci.yml
- run: pip install mastiff
- run: mastiff review origin/main..HEAD --strict --format json
Manual review after a session:
After Claude Code completes a task in a worktree, review all changes before merging:
mastiff review main..HEAD --profile deep
With Codex CLI
mastiff install --codex
Installs a git post-commit hook. Since Codex CLI applies changes via commits, every commit is automatically reviewed. Existing post-commit hooks are preserved and chained.
Baseline
# Record current findings as baseline
mastiff baseline
# Only new findings are reported from now on
# Regenerate after refactoring
mastiff baseline --rebase
The baseline uses fingerprint-based stable IDs that are independent of line numbers, so minor code shifts don't invalidate existing suppressions.
Configuration
Generate a config file:
mastiff init
This creates mastiff.yaml with documented defaults. Key settings:
api:
provider: null # Auto-detect (claude-code, codex, anthropic, openai)
model: null # null = use provider's default (see below)
detection:
min_confidence: 0.6 # Minimum confidence to report
security:
never_send_paths: # Files never sent to the API
- .env
- "*.pem"
- "*.key"
cost:
max_cost_usd_per_run: 1.00 # Per-run cost limit
Model configuration:
When api.model is null (default), each provider uses its own default:
| Provider | Default model |
|---|---|
claude-code |
Whatever your claude CLI is configured to use |
codex |
Whatever your codex CLI is configured to use (~/.codex/config.toml) |
anthropic |
claude-opus-4-20250514 |
openai |
gpt-4.1 |
To override, set api.model to any model your provider supports:
api:
provider: anthropic
model: claude-sonnet-4-20250514
All config models use Pydantic extra="forbid", so typos in config keys are caught immediately.
Security & Privacy
Mastiff sends code to an LLM for analysis (via API or CLI). Here is what it does to minimize exposure:
- What is sent: Only the diff is sent — never complete source files. Import tracing may include small fragments from related files, bounded by a token budget.
- Automatic redaction: Built-in regex patterns detect API keys, tokens, passwords, and private key headers. Detected values are replaced with
[REDACTED]before sending. The Redactor also exposes Shannon entropy analysis for identifying high-entropy strings. - File exclusion: The
never_send_pathssetting excludes sensitive file patterns (.env,*.pem,*.key, etc.) by default. These files are filtered out before any API call. - Output sanitization: ANSI escape sequences and control characters are stripped from all output to prevent terminal injection.
- Prompt injection defense: User-supplied data (diffs, context) is wrapped in delimiter tags (
<diff>,<context>) and the system prompt establishes reviewer-only behavior.
This is a best-effort approach to minimize sensitive data exposure. It does not guarantee that no secrets are sent. Review your never_send_paths configuration and consider the sensitivity of your codebase before use.
Cost Control
Approximate cost per review when using API providers (depends on diff size and API pricing):
| Profile | Estimated cost |
|---|---|
| quick | ~$0.01–0.05 |
| standard | ~$0.05–0.30 |
| deep | ~$0.10–0.50 |
CLI providers (claude-code, codex) use your existing subscription — no per-request API charges.
The cost.max_cost_usd_per_run setting (default: $1.00) enforces a per-run budget for API providers.
Migration
From v0.2.0
- CLI providers: Mastiff now auto-detects
claudeandcodexCLIs and prefers them over API keys. If you want to keep using API keys when a CLI is also installed, setapi.provider: anthropic(oropenai) explicitly inmastiff.yaml. - New provider names:
claude-codeandcodexare now valid values forapi.provider.
From v0.1.0
- Exit code change:
--strictnow exits with code 2 (was 1) when findings are present. Update CI scripts that check forexit 1. - New category: The
securitydetection category is enabled by default. If you explicitly list categories inmastiff.yaml, addsecurity: trueto enable it.
Requirements
- Python >= 3.12
- One of:
claudeCLI,codexCLI, Anthropic API key, or OpenAI API key - Git
Optional extras:
pip install "mastiff[tree-sitter]" # Enhanced import tracing
pip install "mastiff[lsp]" # LSP server support
pip install "mastiff[openai]" # OpenAI provider support
Development
git clone <repo> && cd mastiff
uv sync --all-extras
pytest # 325 tests
ruff check . # lint
mypy src/ # type check
Package structure:
src/mastiff/
├── _internal/ # Git and subprocess utilities
├── analysis/ # Categories, prompt building, LLM client
├── cli/ # Commands and terminal output
├── config/ # Schema, loader, defaults
├── context/ # Language parsers, import tracer, resolver
├── core/ # Engine, models, fingerprinting, severity
├── diff/ # Diff parsing, filtering, collection
├── integrations/ # Pre-commit, Claude Code, Codex hooks, LSP server
├── observability/ # Logging and metrics
└── security/ # Secret patterns, redactor, sanitizer
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 mastiff-0.3.3.tar.gz.
File metadata
- Download URL: mastiff-0.3.3.tar.gz
- Upload date:
- Size: 110.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a5b88edc68c031a31d8875a79fe2360a9b845c74f1192d4773716e763dc940c
|
|
| MD5 |
3749717901f9d47b944f66856c251da9
|
|
| BLAKE2b-256 |
93cef447f6ea90b796f0a360714f33d8580681910acf765e7a1e3e7178a7adaf
|
Provenance
The following attestation bundles were made for mastiff-0.3.3.tar.gz:
Publisher:
release.yml on yuuichieguchi/mastiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mastiff-0.3.3.tar.gz -
Subject digest:
7a5b88edc68c031a31d8875a79fe2360a9b845c74f1192d4773716e763dc940c - Sigstore transparency entry: 1050369655
- Sigstore integration time:
-
Permalink:
yuuichieguchi/mastiff@607b999a8ddc76f363e2792d512e3b2d182f7bdd -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/yuuichieguchi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@607b999a8ddc76f363e2792d512e3b2d182f7bdd -
Trigger Event:
push
-
Statement type:
File details
Details for the file mastiff-0.3.3-py3-none-any.whl.
File metadata
- Download URL: mastiff-0.3.3-py3-none-any.whl
- Upload date:
- Size: 57.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1548b0aa93f950a076ec0e74bb8fc890dd29b4a8e060976bebab5c7b5b62f9f4
|
|
| MD5 |
8c31bf8e5bf46de461c50104aaea2caf
|
|
| BLAKE2b-256 |
66608a7db43a2593ef7f412ca39d0cbd2eb1e9e8c2e69cc91f35bec312c703f3
|
Provenance
The following attestation bundles were made for mastiff-0.3.3-py3-none-any.whl:
Publisher:
release.yml on yuuichieguchi/mastiff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mastiff-0.3.3-py3-none-any.whl -
Subject digest:
1548b0aa93f950a076ec0e74bb8fc890dd29b4a8e060976bebab5c7b5b62f9f4 - Sigstore transparency entry: 1050369660
- Sigstore integration time:
-
Permalink:
yuuichieguchi/mastiff@607b999a8ddc76f363e2792d512e3b2d182f7bdd -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/yuuichieguchi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@607b999a8ddc76f363e2792d512e3b2d182f7bdd -
Trigger Event:
push
-
Statement type: