Action Restrictions and Permissions › Principle of Least Privilege
AI Agent HandbookAll chapters 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:
## 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:
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:
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.