Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/en/learn/tool-hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,62 @@ def safety_check(context: ToolCallHookContext) -> bool | None:
return None
```

#### Agent Threat Rules pre-tool scanner

[Agent Threat Rules](https://github.com/Agent-Threat-Rule/agent-threat-rules) (ATR) is a catalog-driven rule set for known AI agent threats such as prompt injection, tool poisoning, context exfiltration, and privilege escalation. A recommended CrewAI integration path is a `@before_tool_call` hook: at this point the agent has already selected a concrete tool and concrete arguments, but the tool has not executed yet.

This keeps ATR outside CrewAI's core dependency tree while giving scanners the full pre-tool context they need:

- `context.tool_name` identifies the intended action
- `context.tool_input` contains the concrete arguments to scan
- `context.agent`, `context.task`, and `context.crew` provide execution context and access to each object's `security_config`
- The hook can return `False` to block execution before a risky tool call runs

```python
from crewai.hooks import ToolCallHookContext, before_tool_call


def scan_with_agent_threat_rules(payload: dict) -> list[str]:
"""Call your ATR adapter and return matched rule IDs."""
# Example adapter boundary. Load ATR/pyatr in your app or optional integration
# package, then pass tool intent plus security metadata to the scanner.
return []


@before_tool_call
def scan_tool_intent_with_atr(context: ToolCallHookContext) -> bool | None:
payload = {
"tool_name": context.tool_name,
"tool_input": context.tool_input,
"agent_role": context.agent.role if context.agent else None,
"agent_fingerprint": (
context.agent.security_config.fingerprint.uuid_str
if context.agent and context.agent.security_config
else None
),
"task_description": context.task.description if context.task else None,
"task_fingerprint": (
context.task.security_config.fingerprint.uuid_str
if context.task and context.task.security_config
else None
),
"crew_fingerprint": (
context.crew.security_config.fingerprint.uuid_str
if context.crew and context.crew.security_config
else None
),
}

matched_rule_ids = scan_with_agent_threat_rules(payload)
if matched_rule_ids:
print(f"Blocked by Agent Threat Rules: {matched_rule_ids}")
return False

return None
```

For credential brokering or secret access checks, attach the credential metadata your application is about to use to the same payload before scanning. That lets ATR-style catalog checks inspect both the action intent and the sensitive context in scope without requiring CrewAI to vendor the ATR rule corpus.

### 2. Human Approval Gate

```python
Expand Down
36 changes: 36 additions & 0 deletions lib/crewai/tests/hooks/test_tool_hooks_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

import ast
from pathlib import Path


DOCS_ROOT = Path(__file__).parents[4] / "docs"
TOOL_HOOKS_DOC = DOCS_ROOT / "en" / "learn" / "tool-hooks.mdx"


def test_tool_hooks_document_agent_threat_rules_integration_path() -> None:
"""Document the pre-tool hook path for Agent Threat Rules scanners."""
content = TOOL_HOOKS_DOC.read_text(encoding="utf-8")

section_start = content.index("#### Agent Threat Rules pre-tool scanner")
section_end = content.index("### 2. Human Approval Gate", section_start)
atr_section = content[section_start:section_end]

assert "Agent Threat Rules" in atr_section
assert "A recommended CrewAI integration path" in atr_section
assert "@before_tool_call" in atr_section
assert "scan_tool_intent_with_atr" in atr_section
assert "scan_with_agent_threat_rules" in atr_section
assert "context.tool_name" in atr_section
assert "context.tool_input" in atr_section
assert "context.agent" in atr_section
assert "context.task" in atr_section
assert "context.crew" in atr_section
assert "agent_fingerprint" in atr_section
assert "task_fingerprint" in atr_section
assert "crew_fingerprint" in atr_section
assert "security_config.fingerprint" in atr_section
assert "return False" in atr_section

python_block = atr_section.split("```python", 1)[1].split("```", 1)[0]
ast.parse(python_block)