@@ -17,12 +17,22 @@ pub struct HardcodedSecurityRules {
pub static HARDCODED_SECURITY_RULES: LazyLock<HardcodedSecurityRules> = LazyLock::new(|| {
HardcodedSecurityRules {
+ // Case-insensitive; `(-[rf]+\s+)*` handles `-rf`, `-fr`, `-RF`, `-r -f`, etc.
terminal_deny: vec![
// Recursive deletion of root - "rm -rf /" or "rm -rf / "
- CompiledRegex::new(r"rm\s+(-[rRfF]+\s+)*/\s*$", false)
+ CompiledRegex::new(r"rm\s+(-[rf]+\s+)*/\s*$", false)
.expect("hardcoded regex should compile"),
- // Recursive deletion of home - "rm -rf ~" (but not ~/subdir)
- CompiledRegex::new(r"rm\s+(-[rRfF]+\s+)*~\s*$", false)
+ // Recursive deletion of home - "rm -rf ~" or "rm -rf ~/" (but not ~/subdir)
+ CompiledRegex::new(r"rm\s+(-[rf]+\s+)*~/?\s*$", false)
+ .expect("hardcoded regex should compile"),
+ // Recursive deletion of home via $HOME - "rm -rf $HOME" or "rm -rf ${HOME}"
+ CompiledRegex::new(r"rm\s+(-[rf]+\s+)*(\$HOME|\$\{HOME\})/?\s*$", false)
+ .expect("hardcoded regex should compile"),
+ // Recursive deletion of current directory - "rm -rf ." or "rm -rf ./"
+ CompiledRegex::new(r"rm\s+(-[rf]+\s+)*\./?\s*$", false)
+ .expect("hardcoded regex should compile"),
+ // Recursive deletion of parent directory - "rm -rf .." or "rm -rf ../"
+ CompiledRegex::new(r"rm\s+(-[rf]+\s+)*\.\./?\s*$", false)
.expect("hardcoded regex should compile"),
],
}
@@ -1065,14 +1075,44 @@ mod tests {
#[test]
fn hardcoded_blocks_rm_rf_root() {
- // rm -rf / should be blocked by hardcoded rules
t("rm -rf /").is_deny();
+ t("rm -fr /").is_deny();
+ t("rm -RF /").is_deny();
+ t("rm -FR /").is_deny();
+ t("rm -r -f /").is_deny();
+ t("rm -f -r /").is_deny();
+ t("RM -RF /").is_deny();
}
#[test]
fn hardcoded_blocks_rm_rf_home() {
- // rm -rf ~ should be blocked by hardcoded rules
t("rm -rf ~").is_deny();
+ t("rm -fr ~").is_deny();
+ t("rm -rf ~/").is_deny();
+ t("rm -rf $HOME").is_deny();
+ t("rm -fr $HOME").is_deny();
+ t("rm -rf $HOME/").is_deny();
+ t("rm -rf ${HOME}").is_deny();
+ t("rm -rf ${HOME}/").is_deny();
+ t("rm -RF $HOME").is_deny();
+ t("rm -FR ${HOME}/").is_deny();
+ t("rm -R -F ${HOME}/").is_deny();
+ t("RM -RF ~").is_deny();
+ }
+
+ #[test]
+ fn hardcoded_blocks_rm_rf_dot() {
+ t("rm -rf .").is_deny();
+ t("rm -fr .").is_deny();
+ t("rm -rf ./").is_deny();
+ t("rm -rf ..").is_deny();
+ t("rm -fr ..").is_deny();
+ t("rm -rf ../").is_deny();
+ t("rm -RF .").is_deny();
+ t("rm -FR ../").is_deny();
+ t("rm -R -F ../").is_deny();
+ t("RM -RF .").is_deny();
+ t("RM -RF ..").is_deny();
}
#[test]
@@ -1080,12 +1120,18 @@ mod tests {
// Even with always_allow_tool_actions=true, hardcoded rules block
t("rm -rf /").global(true).is_deny();
t("rm -rf ~").global(true).is_deny();
+ t("rm -rf $HOME").global(true).is_deny();
+ t("rm -rf .").global(true).is_deny();
+ t("rm -rf ..").global(true).is_deny();
}
#[test]
fn hardcoded_cannot_be_bypassed_by_allow_pattern() {
// Even with an allow pattern that matches, hardcoded rules block
t("rm -rf /").allow(&[".*"]).is_deny();
+ t("rm -rf $HOME").allow(&[".*"]).is_deny();
+ t("rm -rf .").allow(&[".*"]).is_deny();
+ t("rm -rf ..").allow(&[".*"]).is_deny();
}
#[test]
@@ -1097,6 +1143,18 @@ mod tests {
t("rm -rf /tmp/test")
.mode(ToolPermissionMode::Allow)
.is_allow();
+ t("rm -rf ~/Documents")
+ .mode(ToolPermissionMode::Allow)
+ .is_allow();
+ t("rm -rf $HOME/Documents")
+ .mode(ToolPermissionMode::Allow)
+ .is_allow();
+ t("rm -rf ../some_dir")
+ .mode(ToolPermissionMode::Allow)
+ .is_allow();
+ t("rm -rf .hidden_dir")
+ .mode(ToolPermissionMode::Allow)
+ .is_allow();
}
#[test]
@@ -1105,5 +1163,8 @@ mod tests {
t("ls && rm -rf /").is_deny();
t("echo hello; rm -rf ~").is_deny();
t("cargo build && rm -rf /").global(true).is_deny();
+ t("echo hello; rm -rf $HOME").is_deny();
+ t("echo hello; rm -rf .").is_deny();
+ t("echo hello; rm -rf ..").is_deny();
}
}
@@ -71,8 +71,9 @@ pub struct AgentSettingsContent {
/// that you allow it, always choose to allow it.
///
/// **Security note**: Even with this enabled, Zed's built-in security rules
- /// still block some tool actions, such as the terminal tool running `rm -rf /` or `rm -rf ~`,
- /// to prevent certain classes of failures from happening.
+ /// still block some tool actions, such as the terminal tool running `rm -rf /`, `rm -rf ~`,
+ /// `rm -rf $HOME`, `rm -rf .`, or `rm -rf ..`, to prevent certain classes of failures
+ /// from happening.
///
/// This setting has no effect on external agents that support permission modes, such as Claude Code.
///