Action Restrictions and Permissions › Principle of Least Privilege

Michael BrenndoerferAugust 14, 20253 min read
AI Agent HandbookAll chapters
Part 13 of 15Safety and Governance - Keeping the Agent Aligned
Chapter 37Action Restrictions and Permissions › Principle of Least Privilege
Reading section 3 of 11 · Principle of Least Privilege · 3 minSection 3 of 11 · 3 min

Book contents

Section 3 of 11 · 3 min read

Action Restrictions and Permissions › Principle of Least PrivilegeOne section · Sections in this chapter3 / 11 ↓

The first rule of action safety is simple: give your agent the minimum permissions it needs to do its job. This is called the principle of least privilege, and it's a fundamental concept in security.

Let's apply this to our email example. Instead of letting the agent email anyone, we might restrict it to only emailing people in your contacts list, or only emailing specific domains. Here's how:

In[4]:
Code
## Using Claude Sonnet 4.5 for agent reasoning with restricted tools
import os
from anthropic import Anthropic

ALLOWED_DOMAINS = ['example.com', 'trusted-partner.com']
ALLOWED_RECIPIENTS = ['alice@example.com', 'bob@example.com']

def send_email_restricted(to_address, subject, body):
    """Send an email with domain and recipient restrictions"""
    # Check if recipient is in allowed list
    if to_address not in ALLOWED_RECIPIENTS:
        # Check if domain is allowed
        domain = to_address.split('@')[1] if '@' in to_address else ''
        if domain not in ALLOWED_DOMAINS:
            return f"Error: Cannot send email to {to_address}. Not in allowed recipients or domains."
    
    # If we get here, the recipient is allowed
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = 'your.email@example.com'
    msg['To'] = to_address
    
    with smtplib.SMTP('smtp.example.com', 587) as server:
        server.starttls()
        server.login('your.email@example.com', 'PASSWORD')
        server.send_message(msg)
    
    return f"Email sent to {to_address}"

Now the agent can only email specific people or specific domains. If it tries to email someone else, the function returns an error. This is a hard constraint that the agent can't bypass, no matter what the user asks for.

You can apply the same principle to other tools:

File access: Instead of giving the agent access to your entire filesystem, restrict it to a specific directory:

In[5]:
Code
import os

ALLOWED_DIRECTORY = '/home/user/assistant_workspace'

def read_file_restricted(filename):
    """Read a file only from the allowed directory"""
    # Construct the full path
    full_path = os.path.join(ALLOWED_DIRECTORY, filename)
    
    # Make sure the path is actually inside the allowed directory
    # (prevents tricks like "../../../etc/passwd")
    real_path = os.path.realpath(full_path)
    if not real_path.startswith(os.path.realpath(ALLOWED_DIRECTORY)):
        return "Error: Access denied. File is outside allowed directory."
    
    # Check if file exists
    if not os.path.exists(real_path):
        return f"Error: File {filename} not found."
    
    # Read and return the file
    with open(real_path, 'r') as f:
        return f.read()

API calls: Limit which APIs the agent can call and what operations it can perform:

In[6]:
Code
ALLOWED_API_ENDPOINTS = [
    'https://api.weather.com/current',
    'https://api.calendar.com/events'
]

def make_api_call(endpoint, method='GET', data=None):
    """Make an API call to allowed endpoints only"""
    if endpoint not in ALLOWED_API_ENDPOINTS:
        return f"Error: API endpoint {endpoint} is not allowed."
    
    if method not in ['GET', 'POST']:
        return f"Error: HTTP method {method} is not allowed."
    
    # Make the actual API call
    # ... implementation ...

The pattern is consistent: before the tool does anything, it checks whether the action is allowed. If not, it returns an error. The agent sees the error and can explain to the user why the action wasn't possible.