mermaid_cli/utils/
validation.rs1use crate::constants::DANGEROUS_COMMANDS;
6use std::path::Path;
7
8pub fn validate_file_path(path: &str) -> Result<String, String> {
23 if path.contains("..") {
25 return Err(format!("Path contains directory traversal: {}", path));
26 }
27
28 let path_obj = Path::new(path);
30 if path_obj.is_absolute() {
31 return Err(format!("Absolute paths not allowed: {}", path));
32 }
33
34 if path.contains('\0') {
36 return Err("Path contains null byte".to_string());
37 }
38
39 let sensitive_patterns = [
41 ".ssh/",
42 "id_rsa",
43 "id_dsa",
44 "id_ecdsa",
45 "id_ed25519",
46 ".aws/",
47 "credentials",
48 ".env",
49 "config.toml",
50 "/etc/passwd",
51 "/etc/shadow",
52 ];
53
54 for pattern in &sensitive_patterns {
55 if path.contains(pattern) {
56 return Err(format!("Access to sensitive file denied: {}", path));
57 }
58 }
59
60 Ok(path.to_string())
61}
62
63pub fn validate_command(command: &str) -> Result<String, String> {
79 for dangerous in DANGEROUS_COMMANDS {
81 if command.contains(dangerous) {
82 return Err(format!("Dangerous command blocked: {}", dangerous));
83 }
84 }
85
86 if (command.contains('|') && (command.contains("bash") || command.contains("sh")))
88 || command.contains("eval")
89 {
90 return Err("Piping to shell interpreter is not allowed".to_string());
91 }
92
93 if command.contains("$(") || command.contains("`") {
95 return Err("Command substitution detected".to_string());
96 }
97
98 if command.contains(" && ") || command.ends_with("&&") || command.starts_with("&&") {
101 return Err("Command chaining detected".to_string());
102 }
103
104 Ok(command.to_string())
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_validate_file_path_directory_traversal() {
113 assert!(validate_file_path("../secret.txt").is_err());
114 assert!(validate_file_path("../../etc/passwd").is_err());
115 }
116
117 #[test]
118 fn test_validate_file_path_absolute_paths() {
119 assert!(validate_file_path("/etc/passwd").is_err());
120 assert!(validate_file_path("/home/user/.ssh/id_rsa").is_err());
121 }
122
123 #[test]
124 fn test_validate_file_path_sensitive_files() {
125 assert!(validate_file_path(".ssh/id_rsa").is_err());
126 assert!(validate_file_path(".aws/credentials").is_err());
127 assert!(validate_file_path(".env").is_err());
128 }
129
130 #[test]
131 fn test_validate_file_path_valid() {
132 assert!(validate_file_path("src/main.rs").is_ok());
133 assert!(validate_file_path("Cargo.toml").is_ok());
134 assert!(validate_file_path("tests/test.rs").is_ok());
135 }
136
137 #[test]
138 fn test_validate_command_dangerous() {
139 assert!(validate_command("rm -rf /").is_err());
140 assert!(validate_command("mkfs").is_err());
141 }
142
143 #[test]
144 fn test_validate_command_injection() {
145 assert!(validate_command("cargo test && rm -rf /").is_err());
146 assert!(validate_command("echo test | bash").is_err());
147 assert!(validate_command("echo $(cat /etc/passwd)").is_err());
148 }
149
150 #[test]
151 fn test_validate_command_valid() {
152 assert!(validate_command("cargo test").is_ok());
153 assert!(validate_command("cargo build --release").is_ok());
154 assert!(validate_command("rustfmt src/main.rs").is_ok());
155 }
156}