Initial commit
This commit is contained in:
429
references/permissions-guide.md
Normal file
429
references/permissions-guide.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Permissions Guide
|
||||
|
||||
Complete guide to permission control in Claude Agent SDK.
|
||||
|
||||
---
|
||||
|
||||
## Permission Modes
|
||||
|
||||
### Overview
|
||||
|
||||
Three built-in modes:
|
||||
|
||||
| Mode | Behavior | Use Case |
|
||||
|------|----------|----------|
|
||||
| `default` | Standard checks | General use, production |
|
||||
| `acceptEdits` | Auto-approve file edits | Trusted refactoring |
|
||||
| `bypassPermissions` | Skip ALL checks | CI/CD, sandboxed envs |
|
||||
|
||||
---
|
||||
|
||||
## Default Mode
|
||||
|
||||
Standard permission checks.
|
||||
|
||||
```typescript
|
||||
options: {
|
||||
permissionMode: "default"
|
||||
}
|
||||
```
|
||||
|
||||
**Prompts user for**:
|
||||
- File writes/edits
|
||||
- Potentially dangerous bash commands
|
||||
- Sensitive operations
|
||||
|
||||
**Auto-allows**:
|
||||
- Read operations (Read, Grep, Glob)
|
||||
- Safe bash commands
|
||||
|
||||
---
|
||||
|
||||
## Accept Edits Mode
|
||||
|
||||
Automatically approves file modifications.
|
||||
|
||||
```typescript
|
||||
options: {
|
||||
permissionMode: "acceptEdits"
|
||||
}
|
||||
```
|
||||
|
||||
**Auto-approves**:
|
||||
- File edits (Edit)
|
||||
- File writes (Write)
|
||||
|
||||
**Still prompts for**:
|
||||
- Dangerous bash commands
|
||||
- Sensitive operations
|
||||
|
||||
**Use when**: Refactoring, code generation workflows
|
||||
|
||||
---
|
||||
|
||||
## Bypass Permissions Mode
|
||||
|
||||
⚠️ **DANGER**: Skips ALL permission checks.
|
||||
|
||||
```typescript
|
||||
options: {
|
||||
permissionMode: "bypassPermissions"
|
||||
}
|
||||
```
|
||||
|
||||
**Auto-approves EVERYTHING**:
|
||||
- All file operations
|
||||
- All bash commands
|
||||
- All tools
|
||||
|
||||
**ONLY use in**:
|
||||
- CI/CD pipelines
|
||||
- Sandboxed containers
|
||||
- Docker environments
|
||||
- Trusted, isolated contexts
|
||||
|
||||
**NEVER use in**:
|
||||
- Production systems
|
||||
- User-facing environments
|
||||
- Untrusted inputs
|
||||
|
||||
---
|
||||
|
||||
## Custom Permission Logic
|
||||
|
||||
### canUseTool Callback
|
||||
|
||||
```typescript
|
||||
type CanUseTool = (
|
||||
toolName: string,
|
||||
input: any
|
||||
) => Promise<PermissionDecision>;
|
||||
|
||||
type PermissionDecision =
|
||||
| { behavior: "allow" }
|
||||
| { behavior: "deny"; message?: string }
|
||||
| { behavior: "ask"; message?: string };
|
||||
```
|
||||
|
||||
### Basic Example
|
||||
|
||||
```typescript
|
||||
options: {
|
||||
canUseTool: async (toolName, input) => {
|
||||
// Allow read-only
|
||||
if (['Read', 'Grep', 'Glob'].includes(toolName)) {
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
|
||||
// Deny dangerous bash
|
||||
if (toolName === 'Bash' && input.command.includes('rm -rf')) {
|
||||
return {
|
||||
behavior: "deny",
|
||||
message: "Destructive command blocked"
|
||||
};
|
||||
}
|
||||
|
||||
// Ask for confirmation
|
||||
if (toolName === 'Write') {
|
||||
return {
|
||||
behavior: "ask",
|
||||
message: `Create ${input.file_path}?`
|
||||
};
|
||||
}
|
||||
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: Block Destructive Commands
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
if (toolName === 'Bash') {
|
||||
const dangerous = [
|
||||
'rm -rf',
|
||||
'dd if=',
|
||||
'mkfs',
|
||||
'> /dev/',
|
||||
'shutdown',
|
||||
'reboot',
|
||||
'kill -9',
|
||||
'pkill'
|
||||
];
|
||||
|
||||
for (const pattern of dangerous) {
|
||||
if (input.command.includes(pattern)) {
|
||||
return {
|
||||
behavior: "deny",
|
||||
message: `Blocked dangerous command: ${pattern}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Protect Sensitive Files
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
if (toolName === 'Write' || toolName === 'Edit') {
|
||||
const sensitivePaths = [
|
||||
'/etc/',
|
||||
'/root/',
|
||||
'.env',
|
||||
'credentials',
|
||||
'secrets',
|
||||
'config/production',
|
||||
'.ssh/',
|
||||
'private_key'
|
||||
];
|
||||
|
||||
for (const path of sensitivePaths) {
|
||||
if (input.file_path?.includes(path)) {
|
||||
return {
|
||||
behavior: "ask",
|
||||
message: `⚠️ Modify sensitive file: ${input.file_path}?`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: Environment-Based Permissions
|
||||
|
||||
```typescript
|
||||
const environment = process.env.NODE_ENV; // 'development' | 'staging' | 'production'
|
||||
|
||||
canUseTool: async (toolName, input) => {
|
||||
// Production: require approval for everything
|
||||
if (environment === 'production') {
|
||||
if (toolName === 'Bash' || toolName === 'Write' || toolName === 'Edit') {
|
||||
return {
|
||||
behavior: "ask",
|
||||
message: `PRODUCTION: Approve ${toolName}?`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Staging: auto-approve edits
|
||||
if (environment === 'staging') {
|
||||
if (toolName === 'Write' || toolName === 'Edit') {
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
}
|
||||
|
||||
// Development: allow most things
|
||||
if (environment === 'development') {
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 4: Deployment Confirmation
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
if (toolName === 'Bash') {
|
||||
const deploymentPatterns = [
|
||||
'deploy',
|
||||
'kubectl apply',
|
||||
'terraform apply',
|
||||
'helm install',
|
||||
'docker push',
|
||||
'npm publish'
|
||||
];
|
||||
|
||||
for (const pattern of deploymentPatterns) {
|
||||
if (input.command.includes(pattern)) {
|
||||
return {
|
||||
behavior: "ask",
|
||||
message: `🚀 DEPLOYMENT: ${input.command}\n\nProceed?`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 5: Audit Logging
|
||||
|
||||
```typescript
|
||||
const auditLog: Array<{
|
||||
tool: string;
|
||||
input: any;
|
||||
decision: string;
|
||||
timestamp: Date;
|
||||
}> = [];
|
||||
|
||||
canUseTool: async (toolName, input) => {
|
||||
// Log everything
|
||||
console.log(`[${new Date().toISOString()}] ${toolName}`);
|
||||
|
||||
const decision = { behavior: "allow" as const };
|
||||
|
||||
// Store audit log
|
||||
auditLog.push({
|
||||
tool: toolName,
|
||||
input,
|
||||
decision: decision.behavior,
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
// Could also send to external service
|
||||
// await logToDatadog(toolName, input, decision);
|
||||
|
||||
return decision;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Combining Modes with Custom Logic
|
||||
|
||||
```typescript
|
||||
options: {
|
||||
permissionMode: "acceptEdits", // Auto-approve file edits
|
||||
canUseTool: async (toolName, input) => {
|
||||
// But still block dangerous bash
|
||||
if (toolName === 'Bash' && input.command.includes('rm -rf')) {
|
||||
return { behavior: "deny", message: "Blocked" };
|
||||
}
|
||||
|
||||
// Custom logic runs AFTER permission mode
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### ✅ Do
|
||||
|
||||
- Start with `"default"` mode
|
||||
- Use `canUseTool` for fine-grained control
|
||||
- Block known dangerous patterns
|
||||
- Require confirmation for sensitive ops
|
||||
- Log all tool usage for auditing
|
||||
- Test permission logic thoroughly
|
||||
- Use environment-based rules
|
||||
- Implement rate limiting if needed
|
||||
|
||||
### ❌ Don't
|
||||
|
||||
- Use `"bypassPermissions"` in production
|
||||
- Skip permission checks for "trusted" inputs
|
||||
- Allow arbitrary bash without filtering
|
||||
- Trust file paths without validation
|
||||
- Ignore audit logging
|
||||
- Assume AI won't make mistakes
|
||||
- Give blanket approvals
|
||||
|
||||
---
|
||||
|
||||
## Testing Permissions
|
||||
|
||||
```typescript
|
||||
async function testPermissions() {
|
||||
const tests = [
|
||||
{ tool: "Read", input: { file_path: "/etc/passwd" }, expectAllow: true },
|
||||
{ tool: "Bash", input: { command: "rm -rf /" }, expectDeny: true },
|
||||
{ tool: "Write", input: { file_path: ".env" }, expectAsk: true }
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
const decision = await canUseTool(test.tool, test.input);
|
||||
|
||||
if (test.expectAllow && decision.behavior !== 'allow') {
|
||||
console.error(`FAIL: Expected allow for ${test.tool}`);
|
||||
}
|
||||
if (test.expectDeny && decision.behavior !== 'deny') {
|
||||
console.error(`FAIL: Expected deny for ${test.tool}`);
|
||||
}
|
||||
if (test.expectAsk && decision.behavior !== 'ask') {
|
||||
console.error(`FAIL: Expected ask for ${test.tool}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Allow Read-Only
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
if (['Read', 'Grep', 'Glob'].includes(toolName)) {
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
return {
|
||||
behavior: "deny",
|
||||
message: "Read-only mode"
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Require Approval for All
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
return {
|
||||
behavior: "ask",
|
||||
message: `Approve ${toolName}?`
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Block Bash Entirely
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName, input) => {
|
||||
if (toolName === 'Bash') {
|
||||
return {
|
||||
behavior: "deny",
|
||||
message: "Bash execution disabled"
|
||||
};
|
||||
}
|
||||
return { behavior: "allow" };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
Handle permission errors gracefully:
|
||||
|
||||
```typescript
|
||||
for await (const message of response) {
|
||||
if (message.type === 'error') {
|
||||
if (message.error.type === 'permission_denied') {
|
||||
console.log('Permission denied for:', message.error.tool);
|
||||
// Continue with fallback or skip
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**For more details**: See SKILL.md
|
||||
**Template**: templates/permission-control.ts
|
||||
Reference in New Issue
Block a user