CrewAI + Veto

Role-based authorization for CrewAI multi-agent systems. Each crew member operates within defined boundaries. Prevent privilege escalation through delegation, enforce per-role tool access, and audit every action across your agent teams.

The problem with CrewAI agents today

CrewAI orchestrates teams of specialized agents with distinct roles. But "role" is a prompt concept, not an enforcement mechanism. Any agent can call any tool it's given access to, and delegation lets a manager hand tasks to workers without scoping which tools the delegated agent can use. There is no runtime check that the Researcher stays read-only or the Writer doesn't access payment tools.

The security track record is concerning. In March 2026, CERT/CC disclosed four critical CrewAI vulnerabilities (CVE-2026-2275, -2286, -2287, -2285). The code interpreter tool silently falls back to an insecure sandbox when Docker is unavailable, enabling arbitrary code execution via ctypes. SSRF in the RAG search tools allows content acquisition from internal services. File path validation is missing from the JSON loader. These flaws can be triggered through prompt injection, meaning a malicious user input can manipulate agents into exploiting them. No complete patch exists for all four as of April 2026.

No role enforcement

Agent "roles" are prompt strings, not access control. A Researcher agent with access to a delete tool will use it if the LLM decides to. Roles don't constrain tools.

Delegation escalation

When a manager delegates to a worker, the worker inherits tool access without scoping. Delegation becomes a privilege escalation vector.

4 CVEs in March 2026

Sandbox escape, SSRF, RCE, and local file read. All triggerable via prompt injection. CERT/CC advisory VU#221883. No full patch yet.

Before and after Veto

The left tab shows a standard CrewAI setup. Agents have tools but no authorization. The right tab adds Veto with role-based policies inside each tool function.

from crewai import Agent, Task, Crew, Process
from crewai.tools import tool

@tool
def read_documents(query: str) -> str:
    """Search and read documents from the knowledge base."""
    return document_store.search(query)

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a recipient."""
    return email_service.send(to, subject, body)

@tool
def delete_records(table: str, condition: str) -> str:
    """Delete records from a database table."""
    return db.execute(f"DELETE FROM {table} WHERE {condition}")

@tool
def process_payment(amount: float, recipient: str) -> str:
    """Process a payment transaction."""
    return payment_service.charge(amount, recipient)

researcher = Agent(
    role="Researcher",
    goal="Find and analyze relevant information",
    tools=[read_documents],
)

writer = Agent(
    role="Writer",
    goal="Create content based on research",
    tools=[send_email],
)

manager = Agent(
    role="Manager",
    goal="Oversee operations and approve decisions",
    tools=[delete_records, process_payment],
    allow_delegation=True,
)

# Every agent can call its tools without authorization.
# The Manager can delegate to Writer or Researcher.
# Nothing prevents privilege escalation through delegation.
# CrewAI's code interpreter silently falls back to an
# insecure sandbox when Docker is unavailable (CVE-2026-2275).
crew = Crew(
    agents=[researcher, writer, manager],
    tasks=[...],
    process=Process.hierarchical,
    manager_agent=manager,
)

result = crew.kickoff()

Role-based policy configuration

Define what each agent role can do. Researcher gets read-only. Writer can send internal emails with approval for external. Manager gets write access with payment limits. Roles are enforced at the tool call boundary, not in prompts.

veto/policies.yamlyaml
rules:
  # Researcher: read-only access
  - name: researcher_read_only
    description: Researcher can only read documents
    tool: read_documents
    when: context.agent_role == "researcher"
    action: allow

  - name: researcher_no_write
    description: Block researcher from write operations
    tool: [send_email, delete_records, process_payment]
    when: context.agent_role == "researcher"
    action: deny
    message: "Researcher role has read-only access"

  # Writer: can send email, but not to external domains
  - name: writer_internal_email_only
    description: Writer can only email internal addresses
    tool: send_email
    when: context.agent_role == "writer" && !args.to.endswith("@yourcompany.com")
    action: require_approval
    approvers: [content-lead]
    message: "External emails from writer require content lead approval"

  # Manager: full access with limits
  - name: manager_block_production_deletes
    description: Block destructive writes in production
    tool: delete_records
    when: context.environment == "production"
    action: deny
    message: "Destructive operations blocked in production"

  - name: manager_approve_large_payments
    description: Payments over $1,000 need CFO approval
    tool: process_payment
    when: args.amount > 1000
    action: require_approval
    approvers: [cfo]
    timeout: 1h

  - name: manager_block_huge_payments
    description: Hard block on payments over $50,000
    tool: process_payment
    when: args.amount > 50000
    action: deny
    message: "Payments over $50,000 require manual processing"

Quickstart

1

Install

pip install veto crewai
2

Define role-based policies

Create veto/policies.yaml with rules per agent role. Use context.agent_role to scope access.

3

Add veto.guard() to each tool

Call veto.guard() with the agent's role as context. The crew structure, tasks, and process type stay unchanged.

What Veto covers for CrewAI agents

Role-based tool access

Enforce that each agent role can only use specific tools. Researcher: read-only. Writer: email with approval. Manager: payments with limits. Enforced in code, not prompts.

Delegation protection

When a manager delegates, the worker's tool calls still go through Veto with the worker's role context. No privilege escalation through delegation chains.

Human-in-the-loop

Route sensitive tool calls to human approval queues per role. Writers need content lead approval for external emails. Managers need CFO approval for large payments.

Full audit trail

Every tool call logged with agent role, tool name, arguments, decision, and timestamp. See which agent in which crew attempted what, and what was authorized.

Frequently asked questions

How does Veto handle agent delegation in CrewAI?
When a manager delegates a task to a worker, the worker's tool calls go through Veto with the worker's role context, not the manager's. Policies evaluate based on the executing agent's role, preventing privilege escalation through the delegation chain.
Can different agents have different policies for the same tool?
Yes. Policies condition on context.agent_role. The same send_email tool can allow internal emails for writers but require approval for managers sending to external domains. Each rule specifies which roles it applies to.
Does it work with hierarchical and sequential processes?
Yes. Veto validates inside tool functions, so it works regardless of CrewAI's process type: sequential, hierarchical, or consensual. The crew structure and task assignment stay unchanged.
What happens when a tool call is denied?
The tool function returns a string explaining the denial. The agent sees this as a normal tool response and can adjust its approach. All denials are logged with full context for audit.

Related integrations

Your crew, your rules. Authorization built for multi-agent systems.