feat(permissions): implement permission system with plan mode enforcement (M1 complete)
This commit implements the complete M1 milestone (Config & Permissions) including: - New permissions crate with Tool, Action, Mode, and PermissionManager - Three permission modes: Plan (read-only default), AcceptEdits, Code - Pattern matching for permission rules (exact match and prefix with *) - Integration with config-agent for mode-based permission management - CLI integration with --mode flag to override configured mode - Permission checks for Read, Glob, and Grep operations - Comprehensive test suite (10 tests in permissions, 4 in config, 4 in CLI) Also fixes: - Fixed failing test in tools-fs (glob pattern issue) - Improved glob_list() root extraction to handle patterns like "/*.txt" All 21 workspace tests passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
85
crates/platform/permissions/tests/plan_mode.rs
Normal file
85
crates/platform/permissions/tests/plan_mode.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use permissions::{PermissionManager, Mode, Tool, PermissionDecision};
|
||||
|
||||
#[test]
|
||||
fn plan_mode_blocks_write_bash_by_default() {
|
||||
let mgr = PermissionManager::new(Mode::Plan);
|
||||
|
||||
// Plan mode should allow read operations
|
||||
assert_eq!(mgr.check(Tool::Read, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Grep, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Glob, None), PermissionDecision::Allow);
|
||||
|
||||
// Plan mode should ask for write operations
|
||||
assert_eq!(mgr.check(Tool::Write, None), PermissionDecision::Ask);
|
||||
assert_eq!(mgr.check(Tool::Edit, None), PermissionDecision::Ask);
|
||||
|
||||
// Plan mode should ask for Bash
|
||||
assert_eq!(mgr.check(Tool::Bash, None), PermissionDecision::Ask);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_edits_mode_allows_edit_write() {
|
||||
let mgr = PermissionManager::new(Mode::AcceptEdits);
|
||||
|
||||
// AcceptEdits mode should allow read operations
|
||||
assert_eq!(mgr.check(Tool::Read, None), PermissionDecision::Allow);
|
||||
|
||||
// AcceptEdits mode should allow edit/write
|
||||
assert_eq!(mgr.check(Tool::Edit, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Write, None), PermissionDecision::Allow);
|
||||
|
||||
// But still ask for Bash
|
||||
assert_eq!(mgr.check(Tool::Bash, None), PermissionDecision::Ask);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_mode_allows_everything() {
|
||||
let mgr = PermissionManager::new(Mode::Code);
|
||||
|
||||
assert_eq!(mgr.check(Tool::Read, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Write, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Edit, None), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Bash, None), PermissionDecision::Allow);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bash_pattern_matching() {
|
||||
let mut mgr = PermissionManager::new(Mode::Plan);
|
||||
|
||||
// Add a rule to allow "npm test"
|
||||
mgr.add_rule(Tool::Bash, Some("npm test".to_string()), permissions::Action::Allow);
|
||||
|
||||
// Should allow the exact command
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("npm test")), PermissionDecision::Allow);
|
||||
|
||||
// Should still ask for other commands
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("rm -rf /")), PermissionDecision::Ask);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bash_prefix_matching() {
|
||||
let mut mgr = PermissionManager::new(Mode::Plan);
|
||||
|
||||
// Add a rule to allow "npm test:*" (prefix match)
|
||||
mgr.add_rule(Tool::Bash, Some("npm test:*".to_string()), permissions::Action::Allow);
|
||||
|
||||
// Should allow commands matching the prefix
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("npm test:unit")), PermissionDecision::Allow);
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("npm test:integration")), PermissionDecision::Allow);
|
||||
|
||||
// Should not allow non-matching commands
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("npm install")), PermissionDecision::Ask);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deny_rules_take_precedence() {
|
||||
let mut mgr = PermissionManager::new(Mode::Code);
|
||||
|
||||
// Even in Code mode, we can deny specific operations
|
||||
mgr.add_rule(Tool::Bash, Some("rm -rf*".to_string()), permissions::Action::Deny);
|
||||
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("rm -rf /")), PermissionDecision::Deny);
|
||||
|
||||
// But other commands are still allowed
|
||||
assert_eq!(mgr.check(Tool::Bash, Some("ls")), PermissionDecision::Allow);
|
||||
}
|
||||
Reference in New Issue
Block a user