diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 2132a69be..dd4779872 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -346,6 +346,12 @@ The file.mkdir method will make a new directory at `path`. If the parent The file.moveto method moves a file or directory from `src` to `dst`. If the `dst` directory or file exists it will be deleted before being replaced to ensure consistency across systems. +### file.parent_dir + +`file.parent_dir(path: str) -> str` + +The file.parent_dir method returns the parent directory of a give path. Eg `/etc/ssh/sshd_config` -> `/etc/ssh` + ### file.read `file.read(path: str) -> str` diff --git a/implants/lib/eldritch/src/file/mod.rs b/implants/lib/eldritch/src/file/mod.rs index c7b1a6d99..a02cfbe8e 100644 --- a/implants/lib/eldritch/src/file/mod.rs +++ b/implants/lib/eldritch/src/file/mod.rs @@ -9,6 +9,7 @@ mod is_file_impl; mod list_impl; mod mkdir_impl; mod moveto_impl; +mod parent_dir_impl; mod read_impl; mod remove_impl; mod replace_all_impl; @@ -130,6 +131,10 @@ fn methods(builder: &mut MethodsBuilder) { moveto_impl::moveto(old, new)?; Ok(NoneType{}) } + #[allow(unused_variables)] + fn parent_dir(this: &FileLibrary, path: String) -> anyhow::Result { + parent_dir_impl::parent_dir(path) + } #[allow(unused_variables)] fn replace_all(this: &FileLibrary, path: String, pattern: String, value: String) -> anyhow::Result { diff --git a/implants/lib/eldritch/src/file/parent_dir_impl.rs b/implants/lib/eldritch/src/file/parent_dir_impl.rs new file mode 100644 index 000000000..8a15db5a0 --- /dev/null +++ b/implants/lib/eldritch/src/file/parent_dir_impl.rs @@ -0,0 +1,69 @@ +use anyhow::{Context, Result}; +use std::path::PathBuf; + +pub fn parent_dir(path: String) -> Result { + let mut res = PathBuf::from(&path); + res.pop(); + Ok(res + .to_str() + .context("Failed to convert to str")? + .to_string()) +} + +#[cfg(test)] +mod test { + use crate::runtime::Message; + use pb::eldritch::Tome; + use std::collections::HashMap; + + macro_rules! test_cases { + ($($name:ident: $value:expr,)*) => { + $( + #[tokio::test] + async fn $name() { + let tc: TestCase = $value; + + // Run Eldritch (until finished) + let mut runtime = crate::start(tc.id, tc.tome).await; + runtime.finish().await; + + // Read Messages + let mut found = false; + for msg in runtime.messages() { + if let Message::ReportText(m) = msg { + assert_eq!(tc.id, m.id); + assert_eq!(tc.want_text, m.text); + found = true; + } + } + assert!(found); + } + )* + } + } + + struct TestCase { + pub id: i64, + pub tome: Tome, + pub want_text: String, + } + + + test_cases! { + simple_ssh: TestCase{ + id: 123, + tome: Tome{ + eldritch: String::from(r#"print(file.parent_dir(input_params['path']))"#), + #[cfg(not(target_os="windows"))] + parameters: HashMap::from([(String::from("path"),String::from("/etc/ssh/sshd_config"))]), + #[cfg(target_os="windows")] + parameters: HashMap::from([(String::from("path"),String::from("C:\\ProgramData\\ssh\\sshd_config"))]), + file_names: Vec::new(), + }, + #[cfg(not(target_os="windows"))] + want_text: String::from("/etc/ssh\n"), + #[cfg(target_os="windows")] + want_text: String::from("C:\\ProgramData\\ssh\n"), + }, + } +} diff --git a/implants/lib/eldritch/src/runtime/mod.rs b/implants/lib/eldritch/src/runtime/mod.rs index 1a8d7b449..1c6bd7b1a 100644 --- a/implants/lib/eldritch/src/runtime/mod.rs +++ b/implants/lib/eldritch/src/runtime/mod.rs @@ -89,7 +89,7 @@ mod tests { parameters: HashMap::new(), file_names: Vec::new(), }, - want_text: format!("{}\n", r#"["append", "compress", "copy", "exists", "find", "follow", "is_dir", "is_file", "list", "mkdir", "moveto", "read", "remove", "replace", "replace_all", "template", "timestomp", "write"]"#), + want_text: format!("{}\n", r#"["append", "compress", "copy", "exists", "find", "follow", "is_dir", "is_file", "list", "mkdir", "moveto", "parent_dir", "read", "remove", "replace", "replace_all", "template", "timestomp", "write"]"#), want_error: None, }, process_bindings: TestCase {