Prompt Injection Protection
Automatic safety wrapping for results from external/untrusted tools
Web search and other external tools wrap results in safety markers β the model treats them as data, not instructions.
from praisonaiagents import Agent
from praisonaiagents.tools import duckduckgo
agent = Agent(
name="Researcher",
instructions="Research topics safely",
tools=[duckduckgo],
)
agent.start("Find recent AI safety papers")
# duckduckgo results auto-wrapped in <external_tool_result> markersThe user searches the web; external tool output is wrapped so the model treats it as data.
Quick Start
- Simple Usage
Built-in search tools are protected automatically:
python from praisonaiagents import Agent from praisonaiagents.tools import duckduckgo agent = Agent( name="Researcher", instructions="Research topics safely", tools=[duckduckgo], ) agent.start("Find recent AI safety papers") - With Configuration
Mark custom tools as external:
python from praisonaiagents import Agent, register_tool def my_web_lookup(query: str) -> str: """Fetch data from an external API.""" return fetch_from_api(query) register_tool(my_web_lookup, name="my_web_lookup", trust_level="external") agent = Agent( name="Researcher", instructions="Use my_web_lookup for facts", tools=["my_web_lookup"], )
How It Works
Auto-protected built-ins include duckduckgo, web_search, tavily_search, scrape_page, fetch_url, and others in the external tools set. MCP tools can be marked at registration time.
Outbound tool results are wrapped in <external_tool_result>; inbound webhook and hook payloads are wrapped in <external_request_payload> β same idea, opposite direction. See Untrusted Request Fencing for the ingress boundary.
| Condition | Action |
|---|---|
| Trusted tool | Result unchanged |
| External + string β₯ 32 chars | Wrapped in <external_tool_result> |
| External + string < 32 chars | Unchanged (overhead skip) |
| External + dict/list | JSON-serialised then wrapped |
| Inbound webhook / hook payload | Wrapped in <external_request_payload> at ingress |
What happens if the trust decision fails
When the trust registry exists but raises while answering a trust lookup, PraisonAI now fails closed: the tool is treated as external and its result is fenced. Under-fencing an external tool is the exact bug the fence exists to prevent, so a broken registry never quietly downgrades a tool to “trusted”.
The one exception is “no registry at all” (e.g. a slim build): the hardcoded external list is the whole answer there, so unknown tools stay trusted β otherwise every ordinary function tool would be fenced.
| Situation | Result |
|---|---|
| Tool is in the hardcoded external list | External β result is fenced |
Registry exists and says EXTERNAL | External β result is fenced |
Registry exists and says TRUSTED (or None) | Trusted β result passes through |
| Registry exists and raises while answering | External β result is fenced (fail closed) |
| No registry available at all | Trusted (fallback to hardcoded list only) |
Fixed in PR #4969.
Configuration Options
| Option | Type | Default | Description | |
|---|---|---|---|---|
trust_level | "trusted" \ | "external" | "trusted" | Set on register_tool(..., trust_level="external") |
MIN_CONTENT_LENGTH_FOR_WRAPPING | int | 32 | Minimum string length before wrapping | |
| Registry lookup error | β | β | Treated as external (fail closed) |
Programmatic Injection Defense
The auto-wrap above protects tool outputs. To also scan all agent prompts at the hook layer, enable the injection-defense pipeline:
from praisonai.security import enable_injection_defense
hook_ids = enable_injection_defense()This registers hooks on BEFORE_TOOL and BEFORE_AGENT that scan inputs through a 6-check pipeline and block critical threats.
One-liner (both defenses)
from praisonai.security import enable_security
hook_ids = enable_security() # injection defense + audit logBest Practices
Mark external data sources as external
Web APIs, scraping, MCP servers you do not control, and third-party feeds should use trust_level="external".
Do not strip safety markers
Removing <external_tool_result> tags breaks the model’s boundary between data and instructions.
Prefer external when unsure
Wrapping cost is minimal; under-marking exposes you to injection.
Layer with other guards
Combine with Tool Circuit Breaker and input validation for defence in depth.
Scope hooks in multi-tenant hosts
The hook registry is process-global. In multi-tenant servers, hold the returned hook IDs and remove them via praisonaiagents.hooks.remove_hook on teardown, or add per-request hooks manually instead of using the enable_* one-liner.
Related
The agentβagent counterpart β sub-agent output labelled as data
Untrusted Request FencingThe ingress counterpart β webhook and hook payloads fenced as data
Attribution SanitisationNeutralise untrusted platform display names in the prompt prefix
Vendor ToolsWhere third-party connectors live, and why that is not the same as fencing
Tool Circuit BreakerAutomatic tool failure detection and recovery
Security OverviewComplete security features and best practices