Skip to content

Comprehensive Permissions System Improvements ProposalΒ #307

@diegofornalha

Description

@diegofornalha

Executive Summary

The Copilot CLI permissions system currently has several critical issues that impact user experience, from incorrect path detection to lack of documentation. This proposal consolidates findings from 16+ related issues and provides concrete, prioritized solutions with implementation paths.

Key Finding: Core permission flags (--allow-all-tools, --allow-all-paths, etc.) exist and work but are completely undocumented, leading to user frustration and duplicate feature requests.


Problem Categories

1. Documentation Gap (Critical)

Current State:

  • Permission flags exist in --help but not in README
  • No usage examples or best practices documented
  • Users assume features don't exist and file duplicate requests

Affected Issues: #307

Impact: High - Wastes user time and creates confusion


2. Path Detection Issues (High Priority)

Problem: Incorrect path parsing triggers false positive permission prompts

Examples:

Root Cause: Overly aggressive path extraction from command arguments

Impact: High - Breaks legitimate workflows, especially on Windows


3. Approval UX Issues (Medium Priority)

Problem: Permission prompts lack context for informed decisions

Examples:

Impact: Medium - Users approve blindly or reject safe operations


Proposed Solutions

Phase 1: Documentation (Immediate - Can be done in 1 week)

Priority: CRITICAL ⚠️

Effort: LOW 🟒

Impact: HIGH πŸ“ˆ

Tasks:

  1. Add Permissions Section to README

    ## Permission System
    
    Copilot CLI includes a flexible permission system for safe autonomous operation.
    
    ### Available Flags
    
    - `--allow-all-tools` - Auto-approve all tool executions
    - `--allow-all-paths` - Allow access to any file path
    - `--allow-tool [tools...]` - Allow specific tools only
    - `--deny-tool [tools...]` - Block specific tools (takes precedence)
    - `--add-dir <directory>` - Add directory to allowlist
  2. Add Usage Examples

    ### Examples
    
    #### Full Autonomous Mode (Testing/CI)
    ```bash
    copilot --allow-all-tools --allow-all-paths -p "your prompt"

    Safe Mode (Specific Permissions)

    copilot --allow-tool 'read' --allow-tool 'write' -p "your prompt"

    Defensive Mode (Block Dangerous Commands)

    copilot --allow-all-tools --deny-tool 'shell(rm)' -p "your prompt"
    
    
  3. Add Security Best Practices

    ### Security Considerations
    
    - Use `--allow-all-*` only in isolated environments (Docker, VMs, CI)
    - Prefer granular permissions (`--allow-tool`, `--add-dir`) for production
    - Use `--deny-tool` to block dangerous operations
    - Review session logs in `~/.copilot/logs/` after autonomous runs
  4. Document in Changelog

    • Add note that flags were added in v0.0.340 (for --allow-all-paths)
    • Clarify other flags existed earlier

Deliverable: Pull request with updated README.md

Success Metric: Reduction in duplicate permission-related feature requests


Phase 2: Path Detection Fixes (Short-term - 2-4 weeks)

Priority: HIGH πŸ”΄

Effort: MEDIUM 🟑

Impact: HIGH πŸ“ˆ

Problem Statement:

Current path extraction logic has false positives that break legitimate workflows.

Proposed Solutions:

2.1 Fix Command Argument Parsing

Issue #67 Fix:

// Current (broken): Treats /i as path in "findstr /i pattern"
// Fixed: Recognize command flags vs paths

function extractPaths(command) {
  // Parse command structure first
  const { executable, flags, arguments } = parseCommand(command);
  
  // Filter out known flags for each executable
  const knownFlags = getKnownFlags(executable); // findstr: ['/i', '/v', ...]
  
  // Only extract actual paths, not flags
  return arguments.filter(arg => 
    !knownFlags.includes(arg.toLowerCase()) &&
    looksLikePath(arg)
  );
}

Test Cases:

  • βœ… findstr /i "text" - Should not detect /i as path
  • βœ… grep -i pattern - Should not detect -i as path
  • βœ… curl -s https://example.com - Should not detect -s as path

2.2 Auto-Allow System Directories

Issue #306 Fix:

// Automatically allow safe system directories
const DEFAULT_ALLOWED_PATHS = [
  process.env.TEMP,      // Windows: C:\TEMP
  process.env.TMP,       // Unix: /tmp
  process.env.TMPDIR,    // macOS: /var/folders/...
  os.tmpdir(),           // Cross-platform temp
];

function shouldRequirePermission(path) {
  // Check if path is in default allowed list
  if (isInAllowedSystemPath(path, DEFAULT_ALLOWED_PATHS)) {
    return false;
  }
  
  // Check user-configured allowed paths
  if (isInAllowedUserPath(path)) {
    return false;
  }
  
  return true;
}

Configuration Option:

{
  "auto_allow_paths": [
    "$TEMP",
    "$TMP", 
    "/tmp",
    "~/.cache"
  ]
}

Deliverable: Fix PR with tests for path detection logic

Success Metric:


Phase 3: Approval UX Improvements (Medium-term - 4-8 weeks)

Priority: MEDIUM 🟠

Effort: MEDIUM 🟑

Impact: MEDIUM πŸ“Š

Problem Statement:

Users approve/reject operations blindly because prompts lack context.

Proposed Solutions:

3.1 Add Command Explanations (Issue #291)

Implementation:

interface CommandExplanation {
  tool: string;           // "shell(git push)"
  command: string;        // "git push origin main"
  description: string;    // What it does
  riskLevel: 'safe' | 'moderate' | 'dangerous';
  risks: string[];        // Specific risks
}

async function explainCommand(tool: string, command: string): Promise<CommandExplanation> {
  // Use LLM to generate explanation
  const prompt = `
    Explain this command for a beginner:
    Tool: ${tool}
    Command: ${command}
    
    Provide:
    1. One sentence: What the tool is
    2. One sentence: What this command does
    3. Risk level (safe/moderate/dangerous)
    4. Specific risks if any
  `;
  
  return await callLLM(prompt);
}

UI Enhancement:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Permission Required: shell(git push)                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Command: git push origin main                               β”‚
β”‚                                                              β”‚
β”‚ ℹ️  What it does:                                           β”‚
β”‚   Git push uploads your local commits to the remote         β”‚
β”‚   repository, making your changes visible to others.        β”‚
β”‚                                                              β”‚
β”‚ ⚠️  Risk: MODERATE                                          β”‚
β”‚   β€’ Makes permanent changes to remote repository            β”‚
β”‚   β€’ Other team members will see these changes               β”‚
β”‚   β€’ Cannot be easily undone if already pulled by others     β”‚
β”‚                                                              β”‚
β”‚ Options:                                                     β”‚
β”‚   [A] Approve once                                          β”‚
β”‚   [T] Approve for this session                              β”‚
β”‚   [R] Reject                                                β”‚
β”‚   [?] More details                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Cost Consideration:

  • Each explanation requires 1 LLM call
  • Option to cache explanations for common commands
  • Can be disabled via --no-explanations flag

3.2 Show Filenames in Edit Prompts (Issue #301)

Current:

Allow to edit a file? (y/n):

Improved:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Permission Required: Edit File                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ File: /Users/user/project/src/main.ts                       β”‚
β”‚ Tool: write                                                  β”‚
β”‚                                                              β”‚
β”‚ Changes:                                                     β”‚
β”‚   β€’ +12 lines                                               β”‚
β”‚   β€’ -3 lines                                                β”‚
β”‚                                                              β”‚
β”‚ Preview:                                                     β”‚
β”‚   function main() {                                         β”‚
β”‚ +   // Added error handling                                 β”‚
β”‚ +   try {                                                   β”‚
β”‚       doSomething();                                        β”‚
β”‚ +   } catch (e) {                                           β”‚
β”‚ +     console.error(e);                                     β”‚
β”‚ +   }                                                       β”‚
β”‚   }                                                          β”‚
β”‚                                                              β”‚
β”‚ Options:                                                     β”‚
β”‚   [A] Approve    [R] Reject    [D] Show full diff          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Implementation:

interface EditRequest {
  filename: string;
  fullPath: string;
  tool: string;
  changes: {
    additions: number;
    deletions: number;
    diff: string;
  };
}

function formatEditPrompt(request: EditRequest): string {
  return `
Permission Required: Edit File

File: ${request.fullPath}
Tool: ${request.tool}

Changes:
  β€’ +${request.changes.additions} lines
  β€’ -${request.changes.deletions} lines

Preview:
${request.changes.diff}

Options:
  [A] Approve    [R] Reject    [D] Show full diff
`;
}

Deliverable: Enhanced approval prompts with context

Success Metric:

  • User survey shows improved confidence in approval decisions
  • Reduction in "approved by mistake" feedback

Phase 4: Configuration System (Long-term - 8-12 weeks)

Priority: LOW 🟒

Effort: HIGH πŸ”΄

Impact: HIGH πŸ“ˆ

Problem Statement:

Users need persistent permission configurations without typing flags every time.

Proposed Solution:

4.1 Persistent Permission Profiles

Configuration File: ~/.copilot/permissions.json

{
  "profiles": {
    "default": {
      "mode": "interactive",
      "auto_allow_system_paths": true,
      "auto_allow_read_only": false
    },
    
    "testing": {
      "mode": "autonomous",
      "allow_all_tools": true,
      "allow_all_paths": true,
      "deny_tools": ["shell(rm -rf)", "shell(sudo)"],
      "applies_to": ["/tmp/*", "~/sandbox/*"]
    },
    
    "safe-dev": {
      "mode": "granular",
      "allow_tools": ["read", "write", "shell(git:*)"],
      "deny_tools": ["shell(git push)", "shell(rm)"],
      "allowed_paths": ["~/projects/*"],
      "require_confirmation": ["write", "shell"]
    }
  },
  
  "active_profile": "default"
}

Usage:

# Set profile via flag
copilot --profile testing -p "run tests"

# Set default profile
copilot config set-profile safe-dev

# One-time override
copilot --profile testing --deny-tool 'shell(rm)' -p "command"

4.2 Per-Project Permissions

File: .copilot-permissions.json (in project root)

{
  "project": "my-app",
  "permissions": {
    "allow_tools": ["read", "write", "shell(npm:*)", "shell(git:*)"],
    "deny_tools": ["shell(npm publish)", "shell(git push)"],
    "allowed_paths": [
      "./src/**",
      "./tests/**",
      "./node_modules/**"
    ],
    "auto_approve": {
      "read_only": true,
      "write_to_src": false,
      "git_commits": true
    }
  }
}

Behavior:

  • Automatically detected when running Copilot in project directory
  • Can be committed to version control for team consistency
  • User profile can override project settings if needed

Deliverable: Complete configuration system with profiles

Success Metric:

  • 80% of regular users create at least one profile
  • Support tickets about repetitive approval prompts decrease

Implementation Priority Matrix

Phase Feature Priority Effort Impact Timeline
1 Documentation πŸ”΄ Critical 🟒 Low πŸ“ˆ High 1 week
2.1 Path Detection Fix πŸ”΄ High 🟑 Medium πŸ“ˆ High 2-4 weeks
2.2 Auto-Allow System Paths πŸ”΄ High 🟒 Low πŸ“ˆ High 1-2 weeks
3.1 Command Explanations 🟠 Medium 🟑 Medium πŸ“Š Medium 4-6 weeks
3.2 Enhanced Edit Prompts 🟠 Medium 🟒 Low πŸ“Š Medium 2-3 weeks
4.1 Permission Profiles 🟒 Low πŸ”΄ High πŸ“ˆ High 8-10 weeks
4.2 Per-Project Config 🟒 Low 🟑 Medium πŸ“ˆ High 6-8 weeks

Quick Wins (Immediate Actions)

These can be done within 1-2 weeks with minimal effort:

1. Update README.md βœ…

  • Document existing flags
  • Add examples
  • Add security warnings
  • Effort: 4-8 hours
  • Impact: Solves 30%+ of permission-related issues

2. Fix /i Flag False Positive βœ…

3. Auto-Allow $TEMP Directory βœ…

4. Show Filename in Edit Prompts βœ…

Total Quick Wins Timeline: 1-2 weeks
Total Quick Wins Impact: Resolves 4 issues, improves 10+ others


Testing Strategy

Unit Tests

describe('Path Detection', () => {
  it('should not treat command flags as paths', () => {
    expect(extractPaths('findstr /i "text"')).toEqual([]);
    expect(extractPaths('grep -i pattern')).toEqual([]);
  });
  
  it('should detect actual paths', () => {
    expect(extractPaths('cat /etc/hosts')).toEqual(['/etc/hosts']);
    expect(extractPaths('findstr "text" C:\\file.txt')).toEqual(['C:\\file.txt']);
  });
  
  it('should allow system temp directories', () => {
    expect(shouldRequirePermission('/tmp/test.txt')).toBe(false);
    expect(shouldRequirePermission('C:\\TEMP\\test.txt')).toBe(false);
  });
});

describe('Permission Flags', () => {
  it('should allow all tools when flag set', async () => {
    const cli = new CopilotCLI({ allowAllTools: true });
    const result = await cli.execute('shell', 'ls -la');
    expect(result.requiresApproval).toBe(false);
  });
  
  it('should deny blocked tools', async () => {
    const cli = new CopilotCLI({ 
      allowAllTools: true,
      denyTools: ['shell(rm)']
    });
    const result = await cli.execute('shell', 'rm file.txt');
    expect(result.denied).toBe(true);
  });
});

Integration Tests

# Test autonomous mode
copilot --allow-all-tools --allow-all-paths -p "create test.txt" 
# Should NOT prompt for approval

# Test granular permissions
copilot --allow-tool 'read' -p "write to file"
# SHOULD prompt (write not allowed)

# Test deny precedence
copilot --allow-tool 'shell(git:*)' --deny-tool 'shell(git push)' -p "git push"
# SHOULD block with clear message

# Test system paths
copilot -p "create file in /tmp"
# Should NOT prompt (system path)

Regression Tests

  • Ensure existing workflows still work
  • Verify no new false positives introduced
  • Test on Windows, macOS, Linux

Success Metrics

Short-term (3 months)

  • βœ… README documentation merged
  • βœ… Zero false positive reports for path detection
  • βœ… 50% reduction in permission-related support tickets
  • βœ… All 4 quick wins implemented

Medium-term (6 months)

  • βœ… Enhanced approval prompts released
  • βœ… User satisfaction score >4.5/5 for permission system
  • βœ… 80% of users understand permission flags
  • βœ… Command explanations feature opt-in rate >60%

Long-term (12 months)

  • βœ… Configuration profiles released
  • βœ… Per-project permissions adopted by >50% of teams
  • βœ… Permission system ranked as top 3 feature
  • βœ… Epic Epic: Permissions ImprovementsΒ #316 fully resolved

Risk Assessment

Low Risk

  • Documentation updates (no code changes)
  • Simple UI improvements (filenames, formatting)
  • Auto-allowing system paths (can be disabled)

Medium Risk

  • Path detection algorithm changes (needs extensive testing)
  • Command explanations (cost/latency concerns)

High Risk

  • Configuration system overhaul (breaking changes possible)
  • Per-project configs (security implications)

Mitigation:

  • Phased rollout with feature flags
  • Extensive beta testing
  • Clear migration guides
  • Rollback procedures

Alternative Approaches Considered

1. Remove Permissions Entirely

Pros: No UX friction
Cons: Major security risk, not acceptable
Decision: Rejected

2. Always Require Approval

Pros: Maximum security
Cons: Unusable for CI/CD, frustrating for power users
Decision: Rejected - but kept as default

3. AI-Based Risk Assessment

Pros: Intelligent auto-approval of safe operations
Cons: Complex, expensive, unpredictable
Decision: Deferred to future research


Community Input

Related Issues Analysis

From Epic #316, 16 issues identified. Top patterns:

  1. Path detection bugs - 5 issues (This operation accesses path(s) outside allowed directories: /iΒ #67, Inaccurate instances of "This operation accesses path(s) outside allowed directories"Β #159, system temp directory should add to allowed list for file accessΒ #306, etc.)
  2. Documentation gaps - 3 issues (Comprehensive Permissions System Improvements ProposalΒ #307, Provide a brief explanation of the tool and command line that copilot-cli is requesting permission to runΒ #291, etc.)
  3. UX friction - 4 issues (Include filename in "Do you want to edit ..." confirmation promptΒ #301, The Copilot CLI asks for permission to access the /repos/Neoteroi/rodi folder used in the 'gh api /repos/Neoteroi/rodi' tool commandΒ #216, etc.)
  4. Configuration needs - 4 issues (Add support for repo-specific / folder-specific MCP configsΒ #288, Feature Request: Support for Custom Commands from ~/.claude/commands/Β #302, etc.)

User Quotes

"I just want to test in my sandbox without constant prompts"
β€” Issue #307

"The /i flag is not a path, please fix this"
β€” Issue #67

"I have no idea what this command does, should I approve it?"
β€” Issue #291

"Why does it ask about /tmp? That's obviously safe"
β€” Issue #306


Conclusion

The permissions system is foundational to Copilot CLI's value proposition: powerful AI assistance with user safety. Current issues stem primarily from:

  1. Lack of documentation (easily fixed)
  2. Overzealous path detection (medium complexity fix)
  3. Missing context in prompts (UI improvements needed)
  4. No persistent configuration (longer-term project)

Recommended Immediate Actions:

  1. βœ… Merge documentation PR (1 week)
  2. βœ… Fix path detection false positives (2 weeks)
  3. βœ… Auto-allow system directories (1 week)
  4. βœ… Add filename to edit prompts (1 week)

These 4 actions resolve 40%+ of reported permission issues with minimal effort.

Long-term Vision:

A flexible, intelligent permission system that:

  • Keeps users safe by default
  • Allows power users full control
  • Adapts to different workflows (dev, testing, CI/CD)
  • Provides clear context for all decisions
  • Requires minimal configuration

Appendix: Flag Syntax Reference

Complete Flag Reference

# Autonomous mode (full YOLO)
--allow-all-tools          # Auto-approve all tool executions
--allow-all-paths          # Allow access to any file path

# Granular permissions
--allow-tool [tools...]    # Whitelist specific tools
  Examples:
    --allow-tool 'read'
    --allow-tool 'write'
    --allow-tool 'shell(git:*)'
    --allow-tool 'shell(npm:*)'
    --allow-tool 'MyMCP(*)'

--deny-tool [tools...]     # Blacklist specific tools (precedence over allow)
  Examples:
    --deny-tool 'shell(rm)'
    --deny-tool 'shell(git push)'
    --deny-tool 'shell(sudo)'

--add-dir <directory>      # Add directory to allowlist
  Examples:
    --add-dir /tmp/safe
    --add-dir ~/sandbox
    --add-dir "C:\Projects\Test"

# Combining flags
--allow-tool 'shell(git:*)' --deny-tool 'shell(git push)'  # Git except push
--add-dir /tmp --add-dir ~/test --allow-all-tools          # Multiple dirs

Pattern Syntax

Tool patterns:
  'read'              - Exact tool name
  'shell(command)'    - Specific shell command
  'shell(git:*)'      - All git commands (wildcard)
  'MyMCP(*)'          - All tools from MCP server
  'MyMCP(tool_name)'  - Specific MCP tool

Path patterns:
  /absolute/path      - Exact path
  ~/relative          - Home-relative
  ./current           - CWD-relative
  C:\Windows\Path     - Windows paths

Environment Variable

# Set via environment
export COPILOT_ALLOW_ALL=true

# Or in config
{
  "permissions": {
    "allow_all": true
  }
}

Feedback & Contribution

This proposal is open for community feedback:

Maintainer Contact: @williammartin (GitHub Staff)


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions