Overview
CrewAI agents can call tools that take real actions. Untrusted text in the model context can change those actions. This page shows how to limit that risk. Related reference: OWASP Top 10 for LLM Applications (prompt injection and excessive agency). CrewAI gives you building blocks: hooks, guardrails, structured outputs, and Flow state. It does not turn these on as a secure default. You must set tools, allowlists, and approval checks in your application code. Human-in-the-loop (HITL) is approval, not a control. It pauses for a person to accept, reject, or comment. It does not authenticate the approver, check their role, or prove they were allowed to decide. This page covers threat model and execution-path behavior. For execution limits (max_rpm, max_iter, max_execution_time), verbosity, and agent settings, see Agents and Customize Agents.
Controls by execution path
CrewAI has two common execution paths. Some controls work on only one path.agent.kickoff()
Agent.kickoff() runs an AgentExecutor. It does not create a Task or a Crew. It returns LiteAgentOutput.
@on methods on a @CrewBase class are added to the global hook list when you create that crew. After that, those hooks can also run on later agent.kickoff() calls in the same process. They are not limited to one crew.
See Direct agent interaction.
Crew and Flow
Crew and Flow kickoffs can use Task guardrails, Taskhuman_input, and execution boundary hooks. Tool hooks and LLM hooks also apply.
1. Trusted vs untrusted inputs
Mark every input that reaches the model as trusted or untrusted.
Rules:
- A label in the prompt does not stop the model from following untrusted text. Use code controls.
- Do not add untrusted text to system-level instructions. Keep it in a marked section.
- Give each agent only the fields it needs.
- Load credentials in tool code from the environment or a secrets manager. Do not put them in prompts, memory, or tool arguments that the model builds.
- Enforce policy in code (tool hooks, argument allowlists, guardrails).
backstory text is a soft control. It does not stop the model from following untrusted text. Use tool hooks and allowlists below to enforce policy.
For Crew and Flow inputs, use execution boundary hooks (INPUT). Those hooks do not run on standalone agent.kickoff(). For MCP, see MCP Security.
2. Prompt injection
Prompt injection is untrusted text that tries to override agent instructions. Examples include: ignore prior rules, call tools, leak data, or change the task. Examples:- “Ignore all previous instructions and…”
- “You are now in developer mode…”
- Encoded or multilingual instructions aimed at filters
- Requests to reveal the system prompt or forward private context
Do not rely on prompt wording alone. Limit what the agent can do after the model is steered.
3. Indirect prompt injection
Indirect prompt injection places instructions in content the agent fetches later. The instructions are not in the user message. They can sit in a web page, email, PDF, ticket, or RAG chunk. Example:- The user asks the agent to summarize a vendor page and draft an outreach email.
- Scrape or search returns page text that says to BCC an attacker and attach API keys.
- The agent follows that text when it drafts or sends the email.
- Give research agents read and fetch tools only. Give action agents tools that send, write, or change data only.
- Pass validated structured state between them. Do not pass raw tool output.
- Allowlist destinations in tool hooks (domains; block private and link-local ranges where needed).
- For MCP tool metadata injection, see MCP Security.
4. Tool abuse
Tool abuse is use of a valid tool in a harmful way. Examples: delete data, export data, spend money, send a message, or run code.- Give each agent only the tools its role needs.
- Constrain arguments in code.
- Prefer short-lived, per-tool credentials. Do not share one high-privilege account.
tools= on @on is matched after sanitize_tool_name (lowercase, underscored). Use the sanitized tool name (for example send_email, or file_writer_tool for FileWriterTool).
When a tool call is blocked, the tool does not run. The agent receives a message that the tool was blocked. The run continues. POST_TOOL_CALL still runs on blocked calls.
Use POST_TOOL_CALL to clean results if you need to. That step is optional. See Tool Hooks.
5. Output validation
Check output before you hand it off, store it, take a side effect, or return it from an API.output_pydantic and output_json check schema shape only. They do not check policy. Add a guardrail callable when you need intent or business rules.
Task path (Crew)
agent.kickoff() path
Use Agent.guardrail / guardrail_max_retries. You can also pass response_format= on kickoff(). Agent.guardrail does not run during Crew Task execution.
String or LLMGuardrail checks work on both the Task path and the kickoff path. Crew and Flow runs can also use execution boundary hooks.
6. Approval gates
HITL is approval, not a control. It asks a person to accept or reject. It does not authenticate that person, check their role, or record that they were authorized. Default consoleinput() accepts whoever is at the keyboard.
Require approval before irreversible, expensive, or public actions. Put the pause in code. Do not rely on the prompt alone.
Task
human_input=True pauses after the agent has run its tools and produced a result. It reviews the final answer before that output is accepted. It does not gate tool execution. An agent on that task can still call destructive tools before any human sees the run. Use it only when post-run output review is enough. See Human input on execution.
For approval before a tool runs, use a tool hook and HookAborted:
request_human_input is still approval. It does not validate who typed yes. Add your own identity or policy check if you need that.
Other options:
- Task
human_input=True— post-run output review on the Task / Crew path only. ToolCallHookContext.request_human_input— works onagent.kickoff()and Crew runs. By default it uses a blocking consoleinput().@human_feedback/ Enterprise HITL webhooks — Human-in-the-Loop, Human Feedback in Flows. Same limit: CrewAI does not verify the approver unless you add that outside these APIs.
7. Limiting delegation
allow_delegationdefaults toFalse. Set it toTrueonly when agents must collaborate.- You cannot allow delegation to some agents and block it for others. The limits are crew membership and each agent’s
tools. - Hierarchical process sets
manager_agent.allow_delegation = True. Keep high-risk tools on specialist agents. Put those tools behind hooks or approvals. - For A2A, prefer
A2AClientConfig. Keeptrust_remote_completion_status=Falseunless you want to trust remote completion status. See A2A Agent Delegation.
8. Isolation between agents
- Split read and write access across agents. Example: a researcher reads; an actor sends or writes.
- Use separate crews or Flow steps for untrusted intake and privileged action.
- Pass validated structured state between steps. Do not pass raw tool output.
- Limit knowledge with per-agent
knowledge_sources. For memory, give the agent its ownMemoryorMemoryScope, or turn memory off on the crew. On the Task path,memory=Falseon an agent becomesNone. The agent then uses crew memory if the crew has memory enabled. - Run code in an external sandbox such as E2B tools or Modal. Treat sandbox output as untrusted.
CodeInterpreterToolis removed.allow_code_executionis deprecated and no longer attaches a code tool. - Connect only to MCP servers you trust. See MCP Security.
Related guides
Crafting Effective Agents
Roles, goals, and backstories for specialized agents.
Production Architecture
Flows, guardrails, and structured outputs.
Tool Hooks
Policy checks and approval around tool calls.
MCP Security
Trust, metadata injection, and transport for MCP.
Task Guardrails
Validate task outputs before they continue.
Human-in-the-Loop
Human review of task output and tool calls.
Customize Agents
Execution limits, verbosity, and agent settings.
