Skip to content

What Permissions Should I Give My AI Agent? The Principle of Least Privilege

I gave my AI agent read access to ~/.ssh because I thought it might need to deploy code someday.

Three weeks later, a malicious skill I installed from a public repository exfiltrated my private keys.

The agent wasn’t hacked. It did exactly what I told it to do—access files in ~/.ssh. The skill was designed to steal keys. My permission decision created the vulnerability.

The Problem with “Might Be Useful” Permissions

When setting up AI agents, developers often think:

  • “Maybe it’ll need to read config files from ~/.aws?”
  • “What if I want it to access my environment variables?”
  • “Let’s give it full filesystem read access just in case.”

This thinking is backwards.

An agent with access to only two specific folders and one API cannot leak your entire filesystem through a malicious skill. An agent with no write permissions on critical paths cannot be used to modify production data through a prompt injection.

The attack surface of an AI agent isn’t a bug in the code. It’s the designed behavior when pointed at inputs the designer didn’t anticipate.

What Least Privilege Actually Means for AI Agents

Traditional least privilege: “Give users minimum permissions to do their job.”

AI agent least privilege: “Give the agent access only to resources it needs for its defined tasks—not what might be useful someday.”

Here’s the key difference:

Traditional App: Attackers exploit bugs in code
→ Fix the bug, problem solved
AI Agent: Attackers exploit designed capabilities
→ Can't "fix" capabilities without breaking functionality
→ Must LIMIT capabilities instead

A database query endpoint is designed to accept queries. An AI agent is designed to execute with its granted permissions. Neither is a bug. Both become vulnerabilities when an attacker finds unexpected ways to use them.

Real-World Permission Scoping

Let me show you three concrete scenarios:

Scenario 1: Code Assistant Agent

✅ NEEDS:
- Read/Write: ~/projects/myapp/src
- Network: api.openai.com, github.com
❌ DOES NOT NEED:
- Read: ~/.ssh, ~/.aws, ~/.env
- Write: /etc, ~/.config, ~/.ssh
- Network: *.internal.company.com

If a malicious skill tries to read ~/.ssh/id_rsa, the agent simply cannot access it. No amount of prompt injection or social engineering changes that.

Scenario 2: Documentation Generator

✅ NEEDS:
- Read: ~/docs/source
- Write: ~/docs/output
❌ DOES NOT NEED:
- Any network access
- Any execution permissions (shell commands)
- Read access outside ~/docs/source

This agent cannot exfiltrate data. It cannot modify source code. It cannot reach external servers. It can only read documentation and write documentation.

Scenario 3: Data Pipeline Agent

✅ NEEDS:
- Read: ~/data/input
- Write: ~/data/output
- Network: internal-api.company.com:443
❌ DOES NOT NEED:
- Read: ~/personal, ~/.credentials
- Write: ~/personal, /etc
- Network: External services, cloud providers

Even if an attacker compromises the internal API response to inject malicious instructions, the agent has nowhere to send exfiltrated data and no access to sensitive personal files.

How to Implement Least Privilege

Start with nothing, add only what’s needed:

openclaw-config.yaml
agent:
name: "docs-generator"
# Start with DENY ALL
permissions:
filesystem:
read: [] # Add only specific paths
write: [] # Add only specific paths
network:
allow: [] # Add only specific domains
execution:
allowed: false # Default to no shell access
# Then explicitly grant minimum needed
permissions:
filesystem:
read:
- ~/projects/myapp/docs
write:
- ~/projects/myapp/docs/output
network:
allow: [] # No network needed for this agent
execution:
allowed: false

The configuration above is explicit. No guessing. No “let me check what it has access to.”

The Blast Radius Test

Ask yourself: “If this agent gets compromised, what’s the maximum damage?”

Permission LevelBlast Radius
Full filesystem readAll your files, credentials, secrets
Full filesystem writeRansomware, data destruction, backdoors
Full network accessData exfiltration to any server
Scoped to taskLimited to specific folders/APIs

A compromised agent with least privilege applied can only affect the specific resources it was designed to touch. That’s the entire point.

Common Permission Mistakes

Mistake 1: Inheriting User Permissions

Don’t run agents with your user permissions. Create isolated permission profiles.

Terminal window
# WRONG: Agent inherits your permissions
./agent run # Uses your ~/.aws, ~/.ssh, etc.
# RIGHT: Agent has its own scoped permissions
./agent run --config agent-permissions.yaml

Mistake 2: Granting “Read-Only” Without Scope

Read access to ~ is not “safe.” It includes:

  • ~/.ssh/ - Your private keys
  • ~/.aws/credentials - Your AWS credentials
  • ~/.env files - Your environment variables
  • ~/.config/ - Your application configs

Mistake 3: Assuming Skills Are Trustworthy

You might trust the agent framework, but what about:

  • Third-party skills from public repositories
  • Skills that auto-update
  • Skills with transitive dependencies

A skill runs with the agent’s permissions. If the agent can read ~/.ssh, any skill can read ~/.ssh.

Implementing with OpenClaw

OpenClaw provides permission boundaries at the agent level:

from openclaw import Agent, Permissions
# Define strict permissions
perms = Permissions(
read_paths=["~/projects/myapp/src"],
write_paths=["~/projects/myapp/src"],
allowed_domains=["api.openai.com", "github.com"],
allow_execution=False
)
agent = Agent(
name="code-assistant",
permissions=perms
)
# If compromised, only ~/projects/myapp/src is affected

The permission boundary is enforced at the framework level. Even if a skill or prompt tries to access ~/.aws, the request is denied—not by the skill, not by the model, but by the permission boundary.

When You Need More Permissions

Sometimes your agent genuinely needs broader access:

  • DevOps agent needs cloud provider credentials
  • Data agent needs database connection strings
  • Deployment agent needs SSH keys

Solution: Create separate agents with isolated permissions.

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Code Agent │ │ Deploy Agent │ │ Data Agent │
│ │ │ │ │ │
│ Read: src/ │ │ Read: ~/.ssh │ │ Read: ~/data │
│ Write: src/ │ │ Write: none │ │ Write: output/ │
│ Network: github│ │ Network: prod │ │ Network: db │
└─────────────────┘ └─────────────────┘ └─────────────────┘
↓ ↓ ↓
Limited Isolated Scoped
to code to deployment to data

Each agent has its own permission boundary. Compromising one doesn’t compromise all.

Key Takeaways

  1. Start with nothing - Grant permissions incrementally, not by default
  2. Scope to the task - If the agent is for docs, it doesn’t need code write access
  3. Network is access - Allowed domains are potential data exfiltration channels
  4. Skills inherit permissions - Any installed skill runs with full agent permissions
  5. Test the blast radius - Ask “what’s the worst that can happen?” and reduce accordingly

The principle isn’t about preventing all attacks. It’s about ensuring that when something goes wrong—malicious skill, prompt injection, compromised model—the damage is contained.

Least privilege doesn’t make your agent unhackable. It makes the hack smaller.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments