Tool Permissions & Least Privilege
Your agent starts with read access. Within a month, it has keys to everything.
On this page
The Failure Scenario
A fintech team deploys a support agent with access to their Stripe API to look up payment statuses. The initial key is scoped to read-only charges. Three weeks later, a developer adds a refund-processing feature and swaps in a broader key. That new key can create transfers, modify subscriptions, and delete customers. Nobody updates the permission policy. Nobody reviews the diff.
A user discovers they can ask the agent to "apply a courtesy credit" and the agent happily calls Stripe's create-transfer endpoint. Within 48 hours, $14,000 in unauthorized transfers have been issued. The audit log shows the agent acting within its granted permissions the entire time. The real failure is that those permissions were never supposed to exist.
This is not a hypothetical edge case. It's the default trajectory for any agent that ships without a permission boundary enforced independently of the application code. Developers widen scopes to unblock features. The agent never pushes back.
Why This Matters
AI agents are not users. They do not forget credentials, but they also do not exercise judgment about whether they should use them. When an agent holds a broad API key, every prompt becomes a potential invocation of every capability that key unlocks. The blast radius of a single bad instruction scales linearly with the number of permissions granted.
Least privilege is not a new idea. It's foundational to every serious security framework from SOC 2 to ISO 27001. Agent architectures add a specific failure mode: permission creep without human review. Traditional services have deploy-time reviews, PR approvals, and infrastructure-as-code checks. Agent tool registrations often bypass all of these because they feel like configuration, not code.
The business impact is straightforward. Overprivileged agents create liability exposure, regulatory risk, and reputational damage. If your agent can delete production data, it will eventually delete production data, whether through prompt injection, hallucinated tool calls, or a perfectly reasonable-sounding user request that happens to be malicious.
How to Implement
Start by defining a permission manifest: a declarative file that lists every tool an agent can invoke, what parameters it can pass, and under what conditions. This manifest lives in version control and goes through the same review process as infrastructure changes. No tool call should succeed unless it matches an entry in the manifest.
At runtime, wrap every tool invocation in a permission check that validates the action against the manifest before execution. This is not the same as relying on the downstream API to reject unauthorized calls. The check must happen at the agent layer, before the request leaves your system, so you can log the attempt, alert on anomalies, and enforce rate limits per-tool.
Scope API keys to the narrowest possible permission set. If your agent only reads from a database, the connection string should use a read-only role. If it sends emails, the SMTP credentials should not also grant calendar access. Use short-lived tokens where possible. A 15-minute token that must be refreshed forces a periodic re-evaluation of whether the permission is still needed.
# clawproof-permissions.yaml โ agent permission manifest
agent: support-bot-v2
version: "1.0"
tools:
- name: stripe.charges.retrieve
allowed_params: ["charge_id"]
rate_limit: 60/min
requires_approval: false
- name: stripe.refunds.create
allowed_params: ["charge_id", "amount"]
constraints:
max_amount: 5000 # cents
requires_approval: true
approval_channel: "#agent-approvals"
- name: db.customers.query
allowed_params: ["customer_id", "email"]
mode: read_only
row_limit: 10
denied_tools:
- "stripe.transfers.*"
- "stripe.customers.delete"
- "db.*.write"
- "db.*.delete"Production Checklist
- โEvery agent has a permission manifest checked into version control with required reviewer approval for changes.
- โAPI keys are scoped to the minimum required permissions. No wildcard or admin keys in agent configurations.
- โA runtime permission gateway validates every tool call against the manifest before execution.
- โDenied tool patterns are explicitly listed, not just omitted. This is defense in depth against new tools being added.
- โRate limits are enforced per-tool, not just per-agent, to prevent abuse of any single capability.
- โShort-lived tokens (15-minute expiry or less) are used wherever the downstream API supports them.
- โPermission changes trigger alerts to the security channel and require a separate approval from the feature PR.
- โQuarterly permission audits compare actual tool usage against granted permissions. Revoke anything unused for 30+ days.
Common Pitfalls
Teams most often get tripped up by treating the permission manifest as documentation rather than enforcement. If the manifest says an agent cannot call stripe.transfers.create but nothing actually blocks that call at runtime, you have a policy document, not a security control. The manifest must be the source of truth that the runtime gateway enforces, not a suggestion that developers may or may not follow.
Another frequent failure is scoping permissions at the service level instead of the operation level. Granting "Stripe access" is meaningless from a security perspective. The question is which Stripe operations, with what parameter constraints, at what rate. An agent that can read charges and an agent that can create transfers are fundamentally different risk profiles, even though they both "use Stripe."
Finally, teams often skip denied-tool lists because they assume "if it is not in the allowed list, it is denied." That works until someone adds a new tool integration and forgets to update the manifest. Explicit deny rules catch this. They act as a safety net that fires even when the allow list has gaps.
Terminal Output
$ clawproof --check 01
CHECK 01 โ Tool Permissions & Least Privilege
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[PASS] Permission manifest found: clawproof-permissions.yaml
[PASS] All API keys use scoped credentials (3/3 verified)
[PASS] Runtime permission gateway is active
[PASS] Denied-tool patterns defined (4 patterns)
[PASS] Rate limits configured per-tool
[WARN] Token expiry for Stripe key is 24h โ recommend โค15min
[PASS] Permission change alerts routed to #security
[FAIL] No quarterly audit log found โ last review was 47 days ago
Result: 6 passed, 1 warning, 1 failed
Status: NEEDS ATTENTION