mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Disable default tool permissions (#48278)
Follow-up to https://github.com/zed-industries/zed/pull/48209 - those hardcoded rules are replacing these default settings, which will make the rules clearer by removing the "override" scenario. (No release notes because granular tool permissions are still behind a feature flag.) Release Notes: - N/A
This commit is contained in:
parent
477069ef62
commit
cb647fc482
2 changed files with 19 additions and 179 deletions
|
|
@ -962,75 +962,26 @@
|
|||
// Per-tool permission rules for granular control over tool actions.
|
||||
// This setting only applies to the native Zed agent.
|
||||
"tool_permissions": {
|
||||
// Here are some examples of tool-specific permissions.
|
||||
"tools": {
|
||||
"terminal": {
|
||||
"default_mode": "confirm",
|
||||
"always_deny": [
|
||||
// Dangerous rm commands
|
||||
{ "pattern": "rm\\s+-rf\\s+(/|\\.\\.|\"|~|\\*)" },
|
||||
{ "pattern": "rm\\s+-rf\\s*$" },
|
||||
// Disk destruction
|
||||
{ "pattern": "> /dev/sd" },
|
||||
{ "pattern": "mkfs\\." },
|
||||
{ "pattern": "dd\\s+if=/dev/(zero|random)" },
|
||||
// Fork bomb
|
||||
{ "pattern": ":\\(\\)\\{\\s*:\\|:&\\s*\\};:" },
|
||||
// System files
|
||||
{ "pattern": "/etc/passwd" },
|
||||
{ "pattern": "/etc/shadow" },
|
||||
// Windows destructive commands
|
||||
{ "pattern": "del /f /s /q c:\\\\" },
|
||||
{ "pattern": "format c:" },
|
||||
{ "pattern": "rd /s /q" },
|
||||
],
|
||||
"always_confirm": [
|
||||
// File deletion
|
||||
{ "pattern": "rm\\s" },
|
||||
// Destructive git operations
|
||||
{ "pattern": "git\\s+(reset|clean)\\s+--hard" },
|
||||
{ "pattern": "git\\s+push\\s+(-f|--force)" },
|
||||
// Database operations
|
||||
{ "pattern": "DROP\\s+TABLE", "case_sensitive": true },
|
||||
{ "pattern": "DELETE\\s+FROM", "case_sensitive": true },
|
||||
// Privileged commands
|
||||
{ "pattern": "sudo\\s" },
|
||||
],
|
||||
},
|
||||
"edit_file": {
|
||||
"default_mode": "confirm",
|
||||
"always_deny": [
|
||||
// Secrets and credentials
|
||||
{ "pattern": "\\.env($|\\.)" },
|
||||
{ "pattern": "secrets?/" },
|
||||
{ "pattern": "\\.pem$" },
|
||||
{ "pattern": "\\.key$" },
|
||||
],
|
||||
},
|
||||
"delete_path": {
|
||||
"default_mode": "confirm",
|
||||
"always_deny": [
|
||||
// System directories
|
||||
{ "pattern": "^/etc" },
|
||||
{ "pattern": "^/usr" },
|
||||
{ "pattern": "^/bin" },
|
||||
{ "pattern": "^/sbin" },
|
||||
{ "pattern": "^/var" },
|
||||
{ "pattern": "^/boot" },
|
||||
{ "pattern": "^/root" },
|
||||
// Home directory root
|
||||
{ "pattern": "^~$" },
|
||||
{ "pattern": "^/Users/[^/]+$" },
|
||||
{ "pattern": "^/home/[^/]+$" },
|
||||
// Git directory
|
||||
{ "pattern": "\\.git/?$" },
|
||||
// Windows system directories
|
||||
{ "pattern": "^C:\\\\Windows" },
|
||||
{ "pattern": "^C:\\\\Program Files" },
|
||||
],
|
||||
},
|
||||
"fetch": {
|
||||
"default_mode": "confirm",
|
||||
},
|
||||
// "terminal": {
|
||||
// "default_mode": "confirm",
|
||||
// "always_confirm": [
|
||||
// // Destructive git operations
|
||||
// { "pattern": "git\\s+(reset|clean)\\s+--hard" },
|
||||
// { "pattern": "git\\s+push\\s+(-f|--force)" },
|
||||
// ],
|
||||
// },
|
||||
// "edit_file": {
|
||||
// "default_mode": "confirm",
|
||||
// "always_deny": [
|
||||
// // Secrets and credentials
|
||||
// { "pattern": "\\.env($|\\.)" },
|
||||
// { "pattern": "secrets?/" },
|
||||
// { "pattern": "\\.pem$" },
|
||||
// { "pattern": "\\.key$" },
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
},
|
||||
// When enabled, agent edits will be displayed in single-file editors for review
|
||||
|
|
|
|||
|
|
@ -542,98 +542,6 @@ mod tests {
|
|||
assert_eq!(permissions.invalid_patterns().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_json_tool_permissions_parse() {
|
||||
let default_json = include_str!("../../../assets/settings/default.json");
|
||||
|
||||
let value: serde_json::Value = serde_json_lenient::from_str(default_json)
|
||||
.expect("default.json should be valid JSON with comments");
|
||||
|
||||
let agent = value
|
||||
.get("agent")
|
||||
.expect("default.json should have 'agent' key");
|
||||
let tool_permissions = agent
|
||||
.get("tool_permissions")
|
||||
.expect("agent should have 'tool_permissions' key");
|
||||
|
||||
let content: ToolPermissionsContent = serde_json::from_value(tool_permissions.clone())
|
||||
.expect("tool_permissions should parse into ToolPermissionsContent");
|
||||
|
||||
let permissions = compile_tool_permissions(Some(content));
|
||||
|
||||
let terminal = permissions
|
||||
.tools
|
||||
.get("terminal")
|
||||
.expect("terminal tool should be configured");
|
||||
assert!(
|
||||
!terminal.always_deny.is_empty(),
|
||||
"terminal should have deny rules"
|
||||
);
|
||||
assert!(
|
||||
!terminal.always_confirm.is_empty(),
|
||||
"terminal should have confirm rules"
|
||||
);
|
||||
let edit_file = permissions
|
||||
.tools
|
||||
.get("edit_file")
|
||||
.expect("edit_file tool should be configured");
|
||||
assert!(
|
||||
!edit_file.always_deny.is_empty(),
|
||||
"edit_file should have deny rules"
|
||||
);
|
||||
|
||||
let delete_path = permissions
|
||||
.tools
|
||||
.get("delete_path")
|
||||
.expect("delete_path tool should be configured");
|
||||
assert!(
|
||||
!delete_path.always_deny.is_empty(),
|
||||
"delete_path should have deny rules"
|
||||
);
|
||||
|
||||
let fetch = permissions
|
||||
.tools
|
||||
.get("fetch")
|
||||
.expect("fetch tool should be configured");
|
||||
assert_eq!(
|
||||
fetch.default_mode,
|
||||
settings::ToolPermissionMode::Confirm,
|
||||
"fetch should have confirm as default mode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_deny_rules_match_dangerous_commands() {
|
||||
let default_json = include_str!("../../../assets/settings/default.json");
|
||||
let value: serde_json::Value = serde_json_lenient::from_str(default_json).unwrap();
|
||||
let tool_permissions = value["agent"]["tool_permissions"].clone();
|
||||
let content: ToolPermissionsContent = serde_json::from_value(tool_permissions).unwrap();
|
||||
let permissions = compile_tool_permissions(Some(content));
|
||||
|
||||
let terminal = permissions.tools.get("terminal").unwrap();
|
||||
|
||||
let dangerous_commands = [
|
||||
"rm -rf /",
|
||||
"rm -rf ~",
|
||||
"rm -rf ..",
|
||||
"mkfs.ext4 /dev/sda",
|
||||
"dd if=/dev/zero of=/dev/sda",
|
||||
"cat /etc/passwd",
|
||||
"cat /etc/shadow",
|
||||
"del /f /s /q c:\\",
|
||||
"format c:",
|
||||
"rd /s /q c:\\windows",
|
||||
];
|
||||
|
||||
for cmd in &dangerous_commands {
|
||||
assert!(
|
||||
terminal.always_deny.iter().any(|r| r.is_match(cmd)),
|
||||
"Command '{}' should be blocked by deny rules",
|
||||
cmd
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deny_takes_precedence_over_allow_and_confirm() {
|
||||
let json = json!({
|
||||
|
|
@ -739,25 +647,6 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_json_fork_bomb_pattern_matches() {
|
||||
let default_json = include_str!("../../../assets/settings/default.json");
|
||||
let value: serde_json::Value = serde_json_lenient::from_str(default_json).unwrap();
|
||||
let tool_permissions = value["agent"]["tool_permissions"].clone();
|
||||
let content: ToolPermissionsContent = serde_json::from_value(tool_permissions).unwrap();
|
||||
let permissions = compile_tool_permissions(Some(content));
|
||||
|
||||
let terminal = permissions.tools.get("terminal").unwrap();
|
||||
|
||||
assert!(
|
||||
terminal
|
||||
.always_deny
|
||||
.iter()
|
||||
.any(|r| r.is_match(":(){ :|:& };:")),
|
||||
"Default deny rules should block the classic fork bomb"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compiled_regex_stores_case_sensitivity() {
|
||||
let case_sensitive = CompiledRegex::new("test", true).unwrap();
|
||||
|
|
|
|||
Loading…
Reference in a new issue