Validate a bash command for worktree isolation violations. # Arguments `command` - The bash command to validate `current_stage` - The current stage ID (used to allow access to own worktree) # Returns `ValidationResult::Allowed` if the command is safe `ValidationResult::Blocked(reason)` if the command violates isolation # Examples ``` use loom::hooks::validators::validate_bash_command; // Safe
(command: &str, current_stage: &str)
| 124 | /// assert!(result.is_blocked()); |
| 125 | /// ``` |
| 126 | pub fn validate_bash_command(command: &str, current_stage: &str) -> ValidationResult { |
| 127 | let stripped = strip_embedded_content(command); |
| 128 | let command = &stripped; // Shadow command with stripped version for all checks |
| 129 | |
| 130 | // Check for git -C (directory override) |
| 131 | if GIT_DASH_C_PATTERN.is_match(command) { |
| 132 | return ValidationResult::Blocked(BlockedReason::GitDirectoryOverride); |
| 133 | } |
| 134 | |
| 135 | // Check for git --work-tree (directory override) |
| 136 | if GIT_WORK_TREE_PATTERN.is_match(command) { |
| 137 | return ValidationResult::Blocked(BlockedReason::GitDirectoryOverride); |
| 138 | } |
| 139 | |
| 140 | // Check for ../../ path traversal |
| 141 | if PATH_TRAVERSAL_PATTERN.is_match(command) { |
| 142 | return ValidationResult::Blocked(BlockedReason::PathTraversal); |
| 143 | } |
| 144 | |
| 145 | // Check for .worktrees/ access (allow current stage only) |
| 146 | if let Some(captures) = WORKTREES_ACCESS_PATTERN.captures(command) { |
| 147 | let accessed_stage = captures.get(1).map(|m| m.as_str()).unwrap_or(""); |
| 148 | if accessed_stage != current_stage { |
| 149 | return ValidationResult::Blocked(BlockedReason::CrossWorktreeAccess { |
| 150 | target_stage: Some(accessed_stage.to_string()), |
| 151 | }); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | ValidationResult::Allowed |
| 156 | } |
| 157 | |
| 158 | #[cfg(test)] |
| 159 | mod tests { |