π What Is It?
Excessive Agency is the harm that follows when an LLM system is granted too much ability to act β too many tools, too much permission, or too much autonomy β so that an unexpected, ambiguous, or adversarially manipulated model output triggers a damaging real-world action: an email sent, a row deleted, a payment made. Once a model is wired to tools, its words become actions. It is a design and architecture flaw in the agentic scaffolding, not a flaw inside the model weights β a perfectly benign model can still cause catastrophic damage.
LLM06
OWASP Rank 2025
3 Roots
Functionality Β· Permissions Β· Autonomy
Agentic
Tools Β· Plugins Β· Multi-agent
π± The Three Roots
- Excessive Functionality: the agent can reach tools/capabilities it does not need β every extra tool is extra attack surface.
- Excessive Permissions: tools are scoped far broader than the task (UPDATE/DELETE/DROP when SELECT would do; shared high-privilege service accounts).
- Excessive Autonomy: high-impact, irreversible actions execute with no human confirmation or independent authz check.
β οΈ Top Attack Vectors
- Indirect prompt injection β tool abuse: instructions hidden in a page/doc/email the agent reads hijack its tools.
- Ambiguous output β destructive op: "clean up duplicates" resolves to
delete(filter="*"). - Tool chaining & cascade: search β read secret β HTTP exfiltrate; each call "allowed", the composition is the exploit.
- Over-scoped tools: a "lookup" tool that runs free-form SQL reaches any table.
- Confused deputy / shared creds: agent acts as one privileged identity; per-user authz evaporates.
- Argument injection: attacker text flows into a path, shell fragment, or URL (SSRF).
π΄ Attack Flow
1. Attacker plants text in content the agent reads (page/doc/email/tool result)
β
2. Text enters the model's context (LLM01 trigger)
β
3. Model emits tool_call{ delete_user, {id: 42} }
β
4. No scope check? No approval gate? No downstream authz?
β
5. IMPACT: Real, often irreversible action executes!
β
2. Text enters the model's context (LLM01 trigger)
β
3. Model emits tool_call{ delete_user, {id: 42} }
β
4. No scope check? No approval gate? No downstream authz?
β
5. IMPACT: Real, often irreversible action executes!
β Vulnerable Code
# A "lookup" tool that is really arbitrary SQL over an admin connection
conn = psycopg2.connect("postgres://admin:secret@db/app") # full-privilege
@tool
def query_database(sql: str) -> str:
"""Run a SQL query to answer the user's question."""
with conn.cursor() as cur:
cur.execute(sql) # β ANY statement: SELECT, UPDATE, DROPβ¦
return str(cur.fetchall())
# Injected input β query_database("DELETE FROM orders") irreversible, no gate
β Secure Code
# One narrow capability, read-only role, parameterised, bounded
ro_conn = psycopg2.connect("postgres://agent_ro:pw@db/app") # SELECT only
class OrderStatusArgs(BaseModel):
order_id: int = Field(gt=0) # typed, validated β not free text
@tool(args_schema=OrderStatusArgs)
def get_order_status(order_id: int) -> str:
with ro_conn.cursor() as cur:
cur.execute(
"SELECT status FROM orders WHERE id = %s LIMIT 1", # parameterised
(order_id,),
)
row = cur.fetchone()
return row[0] if row else "not found"
# No other tables, no writes, no arbitrary SQL β the vector is gone.
β Prevention Checklist
- Minimise functionality β explicit, reviewed tool allow-list; no debug/left-over tools
- Least-privilege scope each tool (read vs write vs delete)
- Propagate the end-user's identity; no shared admin creds
- Enforce authorisation downstream, in code β not the model's say-so
- Human-in-the-loop for high-impact / irreversible actions
- Complete mediation: every call through one central policy gate
- Validate/constrain arguments; no model text to shell, eval, or raw SQL
- Rate-limit, budget, sandbox to bound the blast radius
- Fail safe (default-deny, stop and ask); prefer reversible ops
- Log every invocation with the real user; alert on anomalies
π§° Tools & Takeaway
Pydantic
Zod
OAuth scopes
Least-priv DB roles
Approval gate
Egress allow-list
Audit log
π‘ Takeaway: The model proposes, code disposes. Authorisation must live outside the model, at the tool boundary β the security question is never "did the model say the right thing?" but "should THIS call, with THESE args, in THIS context, run at all?"
β οΈ Compounds with: LLM01 Prompt Injection (the trigger) and LLM05 Improper Output Handling (the hand-off). Fixing one without the others leaves the system exposed.