diff --git a/implants/lib/eldritch/src/file/list_impl.rs b/implants/lib/eldritch/src/file/list_impl.rs index 9d6efe9d2..901dd0b3e 100644 --- a/implants/lib/eldritch/src/file/list_impl.rs +++ b/implants/lib/eldritch/src/file/list_impl.rs @@ -211,12 +211,66 @@ pub fn list(starlark_heap: &Heap, path: String) -> Result> { #[cfg(test)] mod tests { + use std::collections::HashMap; + + use crate::runtime::Message; + use super::*; - use tempfile::tempdir; + use pb::eldritch::Tome; + use tempfile::{tempdir, NamedTempFile}; + + fn init_logging() { + pretty_env_logger::formatted_timed_builder() + .filter_level(log::LevelFilter::Info) + .parse_env("IMIX_LOG") + .init(); + } + + #[tokio::test] + async fn test_file_list_file() -> anyhow::Result<()> { + init_logging(); + let tmp_file = NamedTempFile::new()?; + let path = String::from(tmp_file.path().to_str().unwrap()); + let expected_file_name = + String::from(tmp_file.path().file_name().unwrap().to_str().unwrap()); + + // Run Eldritch (until finished) + let mut runtime = crate::start( + 123, + Tome { + eldritch: String::from(r#"print(file.list(input_params['path'])[0]['file_name'])"#), + parameters: HashMap::from([(String::from("path"), path.clone())]), + file_names: Vec::new(), + }, + ) + .await; + runtime.finish().await; + + // Read Messages + let expected_output = format!("{}\n", path); + let mut found = false; + for msg in runtime.messages() { + if let Message::ReportText(m) = msg { + assert_eq!(123, m.id); + assert!(m.text.contains(&expected_file_name)); + log::debug!("text: {:?}", m.text); + found = true; + } + } + assert!(found); + Ok(()) + } + + #[tokio::test] + async fn test_file_list_dir() -> anyhow::Result<()> { + init_logging(); - #[test] - fn test_file_list() -> anyhow::Result<()> { let test_dir = tempdir()?; + let path = test_dir + .path() + .to_str() + .context("Failed to convert string")? + .to_string(); let expected_dirs = ["never gonna", "give you up", ".never gonna"]; let expected_files = ["let_you_down", ".or desert you"]; @@ -230,14 +284,36 @@ mod tests { std::fs::File::create(test_dir_to_create)?; } - let binding = Heap::new(); - let list_res = list(&binding, test_dir.path().to_str().unwrap().to_string())?; - assert_eq!(list_res.len(), (expected_dirs.len() + expected_files.len())); + // Run Eldritch (until finished) + let mut runtime = crate::start( + 123, + Tome { + eldritch: String::from( + r#" +for f in file.list(input_params['path']): + print(f['file_name'])"#, + ), + parameters: HashMap::from([(String::from("path"), path.clone())]), + file_names: Vec::new(), + }, + ) + .await; + runtime.finish().await; + + let mut counter = 0; + for msg in runtime.messages() { + if let Message::ReportText(m) = msg { + counter += 1; + log::debug!("text: {:?}", m.text); + } + } + assert_eq!(counter, (expected_dirs.len() + expected_files.len())); Ok(()) } - #[test] - fn test_file_list_glob() -> anyhow::Result<()> { + + #[tokio::test] + async fn test_file_list_glob() -> anyhow::Result<()> { let test_dir = tempdir()?; let expected_dir = "down the"; let nested_dir = "rabbit hole"; @@ -254,19 +330,51 @@ mod tests { .join(file); std::fs::File::create(test_file)?; - // /tmpdir/down the/* - let binding = Heap::new(); - let list_res = list( - &binding, + // Run Eldritch (until finished) + let mut runtime = crate::start( + 123, + Tome { + eldritch: String::from( + r#" +for f in file.list(input_params['path']): + print(f['file_name'])"#, + ), + parameters: HashMap::from([( + String::from("path"), + test_dir + .path() + .join("*") + .join("win") + .to_str() + .unwrap() + .to_string(), + )]), + file_names: Vec::new(), + }, + ) + .await; + runtime.finish().await; + + let expected_output = format!( + "{}\n", test_dir .path() - .join("*") - .join("win") + .join(expected_dir) + .join(nested_dir) + .join(file) .to_str() .unwrap() - .to_string(), - )?; - println!("{:?}", list_res); + ); + let mut found = false; + for msg in runtime.messages() { + if let Message::ReportText(m) = msg { + assert_eq!(123, m.id); + assert_eq!(expected_output, m.text); + log::debug!("text: {:?}", m.text); + found = true; + } + } + Ok(()) } } diff --git a/tavern/tomes/file_list/main.eldritch b/tavern/tomes/file_list/main.eldritch index 07ebac0b4..2e696cc77 100644 --- a/tavern/tomes/file_list/main.eldritch +++ b/tavern/tomes/file_list/main.eldritch @@ -6,111 +6,24 @@ name = { "Link": "Link" } -SEP = "/" -if sys.get_os().get("platform", "") == "PLATFORM_WINDOWS": - SEP = "\\" - -def can_read(f): - """Return true if the user can read this dir/file - """ - - # Until we get permissions on windows, just go ahead and try to read - if SEP == "\\": - return True - PERM_READ = 4 - f_user = int(f["permissions"][-3]) # User byte - f_group = int(f["permissions"][-2]) # Group byte - - # Check world byte first so it hopefully is fast - if int(f["permissions"][-1]) & PERM_READ: - return True - - # Are we root? - root = usernfo["euid"]["uid"] == 0 - - # If the user isnt root and the user doesnt own the file, clear the user byte - if not root and f["owner"] not in (usernfo["euid"]["name"], usernfo["uid"]["name"]): - f_user = 0 - - # TODO: https://github.com/spellshift/realm/issues/570 - # Will NOT match any group other than primary until #570 is fixed - - # If the user isnt root and the group doesnt own the file, clear the group byte - if not root and f["group"] not in (str(usernfo["egid"]), str(usernfo["gid"])): - f_group = 0 - - if (f_group & PERM_READ) | (f_user & PERM_READ): - return True - return False - -def glob(s, pattern): - """Basic glob functionality""" - p = pattern.split("*") - # Check the first chunk - chunk = p.pop(0) - if not s.startswith(chunk): - return False - s = s[len(chunk):] - # Check the last chunk - if p: - chunk = p.pop() - if chunk: - if not s.endswith(chunk): - return False - s = s[:-len(chunk)] - - # Check all the middle chunks - for part in p: - if part not in s: - return False - s = s[s.index(part)+1:] - return True - -def print_file(path, f): +def print_file(f): """Pretty Print a file""" - full = path.rstrip("/") + "/" + f["file_name"] - if f["type"] == "Directory": - full += "/" - print(f['permissions']+"\t"+f['owner']+"\t"+f['group']+"\t"+str(f['size'])+"\t"+f['modified']+"\t"+name.get(f['type'], f['type'])+"\t"+full+"\n") + perms = f['permissions'] + owner = f['owner'] + group = f['group'] + size = str(f['size']) + modified = f['modified'] + ftype = name.get(f['type'], f['type']) + absolute_path = f['absolute_path'] + + print(f"{perms}\t{owner}\t{group}\t{size}\t{modified}\t{ftype}\t{absolute_path}") def file_list(path): - """List all files in the given path""" - parts = path.strip(SEP).split(SEP) - base = [] # The base of the path that doesnt have a glob - pattern = "" - for p in parts: - if '*' in p: - pattern = p - break - base.append(p) - - base = SEP.join(base) - if SEP == "/": - base = "/" + base - - # Safety checking - if not file.exists(base): - print("Error Path '"+path+"' does not exist\n") - return - elif file.is_file(base): - print("Error Path '"+path+"' is a file\n") - return - - # TODO: No way to check if we can read base - for f in file.list(base): - if pattern == "*": - # List each file/dir in this folder - if f["type"] == "Directory" and can_read(f): - d = base+SEP+f["file_name"] - for f in file.list(d): - print_file(d, f) - elif not pattern: - # Just list each file/folder - print_file(base, f) - elif glob(f["file_name"], pattern): - # Only print files/folders that match the glob - print_file(base, f) + res = file.list(path) + if len(res) > 0: + for f in res: + print_file(f) + else: + eprint(f"No files found at '{path}'") file_list(input_params['path']) -print("\n") -print("\n")