Disable default tool permissions (#48278)

Richard Feldman created

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

Change summary

assets/settings/default.json                |  87 +++--------------
crates/agent_settings/src/agent_settings.rs | 111 -----------------------
2 files changed, 19 insertions(+), 179 deletions(-)

Detailed changes

assets/settings/default.json 🔗

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

crates/agent_settings/src/agent_settings.rs 🔗

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