Skip to main content

elizaos_plugin_code/
path_utils.rs

1use std::path::{Path, PathBuf};
2
3pub const DEFAULT_FORBIDDEN_COMMANDS: [&str; 5] =
4    ["rm -rf /", "rm -rf ~", "sudo rm", "mkfs", "dd if=/dev"];
5
6pub fn extract_base_command(command: &str) -> String {
7    command.split_whitespace().next().unwrap_or("").to_string()
8}
9
10pub fn is_safe_command(command: &str) -> bool {
11    let c = command.trim();
12    if c.is_empty() {
13        return false;
14    }
15    // Block path traversal via cd
16    if c.starts_with("cd ") && c.contains("..") {
17        return false;
18    }
19    if c.contains("&&") || c.contains("||") || c.contains(';') {
20        return false;
21    }
22    if c.contains("$(") || c.contains('`') {
23        return false;
24    }
25    true
26}
27
28pub fn is_forbidden_command(command: &str, additional_forbidden: &[String]) -> bool {
29    let lower = command.to_lowercase();
30    for f in DEFAULT_FORBIDDEN_COMMANDS {
31        if lower.contains(&f.to_lowercase()) {
32            return true;
33        }
34    }
35    for f in additional_forbidden {
36        let ft = f.trim();
37        if ft.is_empty() {
38            continue;
39        }
40        if lower.contains(&ft.to_lowercase()) {
41            return true;
42        }
43    }
44    false
45}
46
47pub fn validate_path(
48    target_path: &str,
49    allowed_directory: &Path,
50    current_directory: &Path,
51) -> Option<PathBuf> {
52    let base = if current_directory.as_os_str().is_empty() {
53        allowed_directory
54    } else {
55        current_directory
56    };
57    let resolved = base.join(target_path);
58
59    // Canonicalize the allowed directory first (best-effort).
60    let allowed = allowed_directory
61        .canonicalize()
62        .ok()
63        .unwrap_or_else(|| allowed_directory.to_path_buf());
64
65    // Canonicalize target if it exists; otherwise canonicalize the parent and re-join.
66    let canonical = match resolved.canonicalize() {
67        Ok(p) => p,
68        Err(_) => {
69            let parent = resolved.parent()?.canonicalize().ok()?;
70            let name = resolved.file_name()?;
71            parent.join(name)
72        }
73    };
74
75    // Enforce containment.
76    if canonical == allowed {
77        return Some(canonical);
78    }
79    canonical.strip_prefix(&allowed).ok()?;
80    Some(canonical)
81}