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:
Richard Feldman 2026-02-03 15:01:15 -05:00 committed by GitHub
parent 477069ef62
commit cb647fc482
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 19 additions and 179 deletions

View file

@ -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

View file

@ -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();