flawopen.com/Reference/Excessive Agency & Tool Scoping

Excessive Agency & Unsafe Tool Scoping

Architectural Risk CWE-250 AI & Agent Security
ELI5 — The Master Ring of Keys

Imagine you hire a temporary intern to organize the office filing cabinets. Instead of giving them the key to just the cabinet, you hand them the master ring of keys that opens the CEO's office, the server room, and the company safe. If the intern makes a simple mistake or is tricked by someone outside, the damage is catastrophic. In Excessive Agency, engineers give AI models raw shell access or broad database admin tools instead of narrow, read-only tools designed for their specific job.

Target: AI agent tool manifests, API bindings, database connectors
Vector: Hallucinated or coerced tool calls executing unrestricted commands
Impact: Production database deletion, mass email spamming, cloud account compromise
Remediation: Principle of least privilege tool design, schema validation, human approval gates

The Mechanism & Root Cause

Developers frequently equip LLMs with high-privilege general-purpose tools like run_shell_command or execute_sql_query to make them versatile. When an LLM hallucinates, misinterprets ambiguity, or encounters prompt injection, it executes irreversible destructive operations with full system authority.

agent_tools.py (Vulnerable Unrestricted Tools)Vulnerable
# VULNERABLE: Giving the LLM raw shell execution powers
@agent.tool
def run_bash_command(command: str):
    # If LLM hallucinates or gets injected with: 'rm -rf /' or 'drop database'
    # It executes directly on the host with the app's permissions!
    return subprocess.check_output(command, shell=True)
agent_tools.py (Hardened Minimal Tool Surface)Hardened
# HARDENED: Domain-specific, parameterized, read-only tools
from pydantic import BaseModel, Field

class OrderLookupSchema(BaseModel):
    order_id: int = Field(..., description="The numeric 6-digit order ID")

@agent.tool(args_schema=OrderLookupSchema)
def lookup_order_status(order_id: int):
    # LLM cannot pass raw SQL or shell commands. Arguments are strictly validated.
    order = db.query(Order).filter(Order.id == order_id).first()
    return {"status": order.status, "updated_at": order.updated_at}

The Attack & Exploit Sequence

Defensive Engineering & Prevention Rules

Explore related security topics and post-mortems: Complete Security Directory →