> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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> markers
```

The user searches the web; external tool output is wrapped so the model treats it as data.

<Note>
  The hook-layer scanner protects agent prompts and tool outputs. The **agent↔agent** boundary is protected separately by the [Inter-Agent Provenance](/docs/features/inter-agent-provenance) envelope — enabled by default on every `create_subagent_tool` call.
</Note>

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Trust-Aware Tool Execution"
        Call[🛠️ Tool call] --> Check{🔍 External?}
        Check -->|Trusted| Pass[⚡ Pass through]
        Check -->|External| Wrap[🛡️ Wrap in markers]
        Wrap --> Safe[✅ Safe result]
        Pass --> Safe
    end

    classDef tool fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef wrap fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class Call tool
    class Check check
    class Wrap,Pass wrap
    class Safe result
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    Built-in search tools are protected automatically:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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")
    ```
  </Step>

  <Step title="With Configuration">
    Mark custom tools as external:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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"],
    )
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Agent
    participant Tool as External tool
    participant Trust as Trust layer
    participant LLM

    Agent->>Tool: Execute
    Tool-->>Trust: Raw result
    Trust-->>Agent: Wrapped in markers
    Agent->>LLM: Treat as factual data only
```

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](/docs/features/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 |

<Warning>
  The hook-level scanner does not read a trust label off the payload. Prompt strings passed through `before_llm_call` are scanned unconditionally so a forged `_source` field cannot bypass the checks. Tool-level trust (external vs internal tool) is still governed by how the tool is registered — that hasn't changed.
</Warning>

***

## 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               |

***

## 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:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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.

<Warning>
  `enable_injection_defense()` registers on the **process-global** `praisonaiagents.hooks` registry — the policy applies to *every* Agent in the process. In a multi-tenant host (e.g. `praisonai serve`) do not rely on it for per-tenant isolation. To scope or tear down later, keep the returned `hook_ids` and remove them via `praisonaiagents.hooks.remove_hook`:

  ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  from praisonaiagents.hooks import remove_hook

  for hid in hook_ids.values():
      remove_hook(hid)
  ```
</Warning>

### One-liner (both defenses)

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.security import enable_security

hook_ids = enable_security()  # injection defense + audit log
```

<Warning>
  `enable_security()` is the same story: it registers **process-global** hooks. One tenant's `extra_patterns` would apply to every tenant sharing the process. Use per-request `add_hook` in multi-tenant hosts, or hold the returned hook IDs and remove them at teardown.
</Warning>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Mark external data sources as external">
    Web APIs, scraping, MCP servers you do not control, and third-party feeds should use `trust_level="external"`.
  </Accordion>

  <Accordion title="Do not strip safety markers">
    Removing `<external_tool_result>` tags breaks the model's boundary between data and instructions.
  </Accordion>

  <Accordion title="Prefer external when unsure">
    Wrapping cost is minimal; under-marking exposes you to injection.
  </Accordion>

  <Accordion title="Layer with other guards">
    Combine with [Tool Circuit Breaker](/docs/features/tool-circuit-breaker) and input validation for defence in depth.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

***

<Note>
  **Related defence.** Tool outputs get wrapped in `<external_tool_result>` markers (this page). **Inbound webhook and hook payloads** get the ingress counterpart — wrapped in `<external_request_payload>`, described in [Untrusted Request Fencing](/docs/features/untrusted-request-fencing). **Platform display names and group titles** interpolated into the `[{sender}] ` attribution prefix get a different treatment — collapse-and-strip — described in [Sender Attribution Sanitisation](/docs/features/prompt-attribution-neutralisation).
</Note>

***

## Related

<CardGroup cols={2}>
  <Card title="Inter-Agent Provenance" icon="shield-check" href="/docs/features/inter-agent-provenance">
    The agent↔agent counterpart — sub-agent output labelled as data
  </Card>

  <Card title="Untrusted Request Fencing" icon="shield-check" href="/docs/features/untrusted-request-fencing">
    The ingress counterpart — webhook and hook payloads fenced as data
  </Card>

  <Card title="Attribution Sanitisation" icon="user-shield" href="/docs/features/prompt-attribution-neutralisation">
    Neutralise untrusted platform display names in the prompt prefix
  </Card>

  <Card title="Tool Circuit Breaker" icon="shield-exclamation" href="/docs/features/tool-circuit-breaker">
    Automatic tool failure detection and recovery
  </Card>

  <Card title="Security Overview" icon="shield" href="/docs/security">
    Complete security features and best practices
  </Card>
</CardGroup>
