From a52455a40e86fabfff4efac8c64a75d34902afaa Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 28 Jun 2026 08:45:36 -0400 Subject: [PATCH 1/6] get_times --- docs/_docs/user-guide/eldritch.md | 26 +++++++++ .../stdlib/eldritch-libfile/src/fake.rs | 18 +++++++ .../stdlib/eldritch-libfile/src/lib.rs | 18 +++++++ .../src/std/get_times_impl.rs | 54 +++++++++++++++++++ .../stdlib/eldritch-libfile/src/std/mod.rs | 5 ++ 5 files changed, 121 insertions(+) create mode 100644 implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 914225d9a..016788679 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -706,6 +706,32 @@ The **file.follow** method will call `fn(line)` for any new `line` that is added file.follow('/home/bob/.bash_history', print) ``` +### file.get_times + +`file.get_times(path: str) -> Dict` + +The **file.get_times** method gets all timestamp metadata of a file/directory. + +On *Windows/Unix*, the **Created, Accessed, Modified** times are returned. On *Unix* targets specifically, the **Changed** time is also returned. + +These times are returned as an integer epoch (SECONDS), as in the number of seconds that elapsed from **Jan 1 1970** + +Example, with **1781444321** being the unix epoch for **Sunday, June 14, 2026 at 1:38:41 PM UTC**: + +```python +file.mkdir("./testdir") +print(file.get_times("./file")) +``` + +```json +{ + "atime": 1781444321, + "crtime": 1781444321, + "ctime": 1781444321, + "mtime": 1781444321 +} +``` + ### file.is_dir `file.is_dir(path: str) -> bool` diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs index 2dc411480..237c9e731 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs @@ -152,6 +152,24 @@ impl FileLibrary for FileLibraryFake { Ok(()) } + fn get_times(&self, path: String) -> Result, String> { + // ensure file existence + if !self.exists(path) { + return Err("File doesn't exist".to_string()); + } + + // create btree + let mut map = BTreeMap::new(); + map.insert("mtime".to_string(), 0); + map.insert("atime".to_string(), 0); + map.insert("crtime".to_string(), 0); + + #[cfg(unix)] + map.insert("ctime".to_string(), 0); + + Ok(map) + } + fn is_dir(&self, path: String) -> Result { let mut root = self.root.lock(); let parts = Self::normalize_path(&path); diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs index e5c69525c..94ea5dfae 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs @@ -115,6 +115,24 @@ pub trait FileLibrary { fn_val: Value, ) -> Result<(), String>; // fn is reserved + #[eldritch_method] + /// Gets the times of a file + /// Modified, Created, and Accessed work on most systems, and for UNIX systems changed time is also outputted. + /// + /// **Parameters** + /// - `path` (`str`): The file to get times from + /// + /// **Returns** + /// - `Dict`: The dictionary of files + /// - `mtime` (`int`) + /// - `atime` (`int`) + /// - `crtime` (`int`) + /// - `ctime` (unix only, `int`) + /// + /// **Errors** + /// - Returns an error upon failure of getting time of file + fn get_times(&self, path: String) -> Result, String>; + #[eldritch_method] /// Checks if the path exists and is a directory. /// diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs new file mode 100644 index 000000000..b48ac3a72 --- /dev/null +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs @@ -0,0 +1,54 @@ +#[cfg(feature = "stdlib")] +extern crate alloc; + +#[cfg(feature = "stdlib")] +use alloc::collections::BTreeMap; + +#[cfg(feature = "stdlib")] +use alloc::string::String; + +#[cfg(feature = "stdlib")] +use std::{fs::{exists, metadata}, time::{SystemTime, UNIX_EPOCH}}; + +#[cfg(feature = "stdlib")] +fn time_to_epoch(time: SystemTime) -> Result { + // get the distance so the time can be represented as an epoch + // epoch is in seconds + let epoch_secs = time.duration_since(UNIX_EPOCH).map_err(|e| e.to_string())?.as_secs().cast_signed(); + Ok(epoch_secs) +} + +#[cfg(feature = "stdlib")] +fn fetch_times(path: String) -> Result, String> { + // check if exists + if !exists(&path).map_err(|e| e.to_string())? { + return Err(alloc::format!("Could not locate {}", path)); + } + + // open the file + let meta = metadata(path).map_err(|e| e.to_string())?; + + // create the output + let mut dict = BTreeMap::new(); + dict.insert("mtime".to_string(), time_to_epoch(meta.modified().map_err(|e| e.to_string())?)?); + dict.insert("atime".to_string(), time_to_epoch(meta.accessed().map_err(|e| e.to_string())?)?); + dict.insert("crtime".to_string(), time_to_epoch(meta.created().map_err(|e| e.to_string())?)?); + + // change time is only available on posix systems + #[cfg(unix)] + use std::os::unix::fs::MetadataExt; + #[cfg(unix)] + dict.insert("ctime".to_string(), meta.ctime()); + + Ok(dict) +} + +#[cfg(feature = "stdlib")] +pub fn get_times(path: String) -> Result, String> { + Ok(fetch_times(path)?) +} + +#[cfg(not(feature = "stdlib"))] +pub fn get_times(path: alloc::string::String) -> Result, alloc::string::String> { + return Err("stdlib required"); +} \ No newline at end of file diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs index de3991ac3..502b20c92 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs @@ -34,6 +34,7 @@ pub mod timestomp_impl; pub mod tmp_dir_impl; pub mod write_binary_impl; pub mod write_impl; +pub mod get_times_impl; #[derive(Debug, Default)] #[eldritch_library_impl(FileLibrary)] @@ -82,6 +83,10 @@ impl FileLibrary for StdFileLibrary { list_impl::list(path) } + fn get_times(&self, path: String) -> Result, String> { + get_times_impl::get_times(path) + } + fn list_named_pipes(&self, detailed: Option) -> Result { list_named_pipes_impl::list_named_pipes(detailed) } From 3cb83a7eab1c0d2fcaa20f0e34de0653d244798c Mon Sep 17 00:00:00 2001 From: meow Date: Wed, 8 Jul 2026 13:39:32 -0400 Subject: [PATCH 2/6] file.list shows timestamp information and directory being listed from --- docs/_docs/user-guide/eldritch.md | 103 ++++++++---------- .../stdlib/eldritch-libfile/src/fake.rs | 18 --- .../stdlib/eldritch-libfile/src/lib.rs | 18 --- .../src/std/get_times_impl.rs | 54 --------- .../eldritch-libfile/src/std/list_impl.rs | 75 +++++++++---- .../stdlib/eldritch-libfile/src/std/mod.rs | 5 - 6 files changed, 102 insertions(+), 171 deletions(-) delete mode 100644 implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 016788679..6842bd87d 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -706,32 +706,6 @@ The **file.follow** method will call `fn(line)` for any new `line` that is added file.follow('/home/bob/.bash_history', print) ``` -### file.get_times - -`file.get_times(path: str) -> Dict` - -The **file.get_times** method gets all timestamp metadata of a file/directory. - -On *Windows/Unix*, the **Created, Accessed, Modified** times are returned. On *Unix* targets specifically, the **Changed** time is also returned. - -These times are returned as an integer epoch (SECONDS), as in the number of seconds that elapsed from **Jan 1 1970** - -Example, with **1781444321** being the unix epoch for **Sunday, June 14, 2026 at 1:38:41 PM UTC**: - -```python -file.mkdir("./testdir") -print(file.get_times("./file")) -``` - -```json -{ - "atime": 1781444321, - "crtime": 1781444321, - "ctime": 1781444321, - "mtime": 1781444321 -} -``` - ### file.is_dir `file.is_dir(path: str) -> bool` @@ -758,40 +732,59 @@ file.list("\\\\127.0.0.1\\c$\\Windows\\*.yml") # List files over UNC paths ``` Each file is represented by a Dict type. -Here is an example of the Dict layout: +Here is an example code snippet using **file.list**: + +```python +print(file.list("/root/some_directory")) +``` ```json [ - { - "file_name": "implants", - "absolute_path": "/workspace/realm/implants", - "size": 4096, - "owner": "root", - "group": "0", - "permissions": "40755", - "modified": "2023-07-09 01:35:40 UTC", - "type": "Directory" + { + "absolute_path": "/root/some_directory", // when listing a directory, it will show the directory itself aswell + "file_name": "some_directory", + "group": "root", + "owner": "root", + "permissions": "40775", + "size": 4096, + "times": { // the timestamps given are provided as seconds past the epoch (Jan 1 1970) + "atime": 1783528762, // accessed time + "crtime": 1783526598, // creation time + "ctime": 1783528762, // changed time (only available on UNIX systems) + "mtime": 1783528762 // modified time }, - { - "file_name": "README.md", - "absolute_path": "/workspace/realm/README.md", - "size": 750, - "owner": "root", - "group": "0", - "permissions": "100644", - "modified": "2023-07-08 02:49:47 UTC", - "type": "File" + "type": "dir" + }, + { + "absolute_path": "/root/some_directory/some_file", + "file_name": "some_file", + "group": "root", + "owner": "root", + "permissions": "40775", + "size": 4096, + "times": { + "atime": 1783526640, + "crtime": 1783526598, + "ctime": 1783526598, + "mtime": 1783526598 }, - { - "file_name": ".git", - "absolute_path": "/workspace/realm/.git", - "size": 4096, - "owner": "root", - "group": "0", - "permissions": "40755", - "modified": "2023-07-10 21:14:06 UTC", - "type": "Directory" - } + "type": "file" + }, + { + "absolute_path": "/root/some_directory/some_other_directory", + "file_name": "some_other_directory", + "group": "root", + "owner": "root", + "permissions": "40775", + "size": 4096, + "times": { + "atime": 1783526640, + "crtime": 1783526598, + "ctime": 1783526598, + "mtime": 1783526598 + }, + "type": "dir" + } ] ``` diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs index 237c9e731..2dc411480 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs @@ -152,24 +152,6 @@ impl FileLibrary for FileLibraryFake { Ok(()) } - fn get_times(&self, path: String) -> Result, String> { - // ensure file existence - if !self.exists(path) { - return Err("File doesn't exist".to_string()); - } - - // create btree - let mut map = BTreeMap::new(); - map.insert("mtime".to_string(), 0); - map.insert("atime".to_string(), 0); - map.insert("crtime".to_string(), 0); - - #[cfg(unix)] - map.insert("ctime".to_string(), 0); - - Ok(map) - } - fn is_dir(&self, path: String) -> Result { let mut root = self.root.lock(); let parts = Self::normalize_path(&path); diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs index 94ea5dfae..e5c69525c 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs @@ -115,24 +115,6 @@ pub trait FileLibrary { fn_val: Value, ) -> Result<(), String>; // fn is reserved - #[eldritch_method] - /// Gets the times of a file - /// Modified, Created, and Accessed work on most systems, and for UNIX systems changed time is also outputted. - /// - /// **Parameters** - /// - `path` (`str`): The file to get times from - /// - /// **Returns** - /// - `Dict`: The dictionary of files - /// - `mtime` (`int`) - /// - `atime` (`int`) - /// - `crtime` (`int`) - /// - `ctime` (unix only, `int`) - /// - /// **Errors** - /// - Returns an error upon failure of getting time of file - fn get_times(&self, path: String) -> Result, String>; - #[eldritch_method] /// Checks if the path exists and is a directory. /// diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs deleted file mode 100644 index b48ac3a72..000000000 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/get_times_impl.rs +++ /dev/null @@ -1,54 +0,0 @@ -#[cfg(feature = "stdlib")] -extern crate alloc; - -#[cfg(feature = "stdlib")] -use alloc::collections::BTreeMap; - -#[cfg(feature = "stdlib")] -use alloc::string::String; - -#[cfg(feature = "stdlib")] -use std::{fs::{exists, metadata}, time::{SystemTime, UNIX_EPOCH}}; - -#[cfg(feature = "stdlib")] -fn time_to_epoch(time: SystemTime) -> Result { - // get the distance so the time can be represented as an epoch - // epoch is in seconds - let epoch_secs = time.duration_since(UNIX_EPOCH).map_err(|e| e.to_string())?.as_secs().cast_signed(); - Ok(epoch_secs) -} - -#[cfg(feature = "stdlib")] -fn fetch_times(path: String) -> Result, String> { - // check if exists - if !exists(&path).map_err(|e| e.to_string())? { - return Err(alloc::format!("Could not locate {}", path)); - } - - // open the file - let meta = metadata(path).map_err(|e| e.to_string())?; - - // create the output - let mut dict = BTreeMap::new(); - dict.insert("mtime".to_string(), time_to_epoch(meta.modified().map_err(|e| e.to_string())?)?); - dict.insert("atime".to_string(), time_to_epoch(meta.accessed().map_err(|e| e.to_string())?)?); - dict.insert("crtime".to_string(), time_to_epoch(meta.created().map_err(|e| e.to_string())?)?); - - // change time is only available on posix systems - #[cfg(unix)] - use std::os::unix::fs::MetadataExt; - #[cfg(unix)] - dict.insert("ctime".to_string(), meta.ctime()); - - Ok(dict) -} - -#[cfg(feature = "stdlib")] -pub fn get_times(path: String) -> Result, String> { - Ok(fetch_times(path)?) -} - -#[cfg(not(feature = "stdlib"))] -pub fn get_times(path: alloc::string::String) -> Result, alloc::string::String> { - return Err("stdlib required"); -} \ No newline at end of file diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs index 5dc38158a..5234f0afc 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs @@ -16,6 +16,8 @@ use nix::unistd::{Gid, Group, Uid, User}; use std::fs; #[cfg(feature = "stdlib")] use std::path::Path; +#[cfg(feature = "stdlib")] +use std::time::UNIX_EPOCH; #[cfg(feature = "stdlib")] pub fn list(path: Option) -> Result>, String> { @@ -53,14 +55,16 @@ fn list_impl(path: String) -> AnyhowResult>> { for entry in glob(&path)? { match entry { Ok(path_buf) => { - // If I implement `handle_list` roughly: + // show information for the file + // if it's a directory, show information of the directory being listed from + final_res.push(create_dict_from_file(&path_buf)?); + + // if it is a directory, also add it's subcontents if path_buf.is_dir() { for entry in fs::read_dir(&path_buf)? { let entry = entry?; final_res.push(create_dict_from_file(&entry.path())?); } - } else { - final_res.push(create_dict_from_file(&path_buf)?); } } Err(e) => eprintln!("Glob error: {e:?}"), @@ -69,6 +73,39 @@ fn list_impl(path: String) -> AnyhowResult>> { Ok(final_res) } +// get the timestamps of a metadata object and return it as a dictionary +#[cfg(feature = "stdlib")] +fn get_times_dict(metadata: std::fs::Metadata) -> Value { + // create dictionary for times data + let mut times: BTreeMap = BTreeMap::new(); + + // add changed time (it's already in epoch format) if we're in unix + #[cfg(unix)] { + use std::os::unix::fs::MetadataExt; + times.insert(Value::String("ctime".to_string()), Value::Int(metadata.ctime())); + } + + // add time information + let timestamps = [ + ("mtime", metadata.modified()), + ("crtime", metadata.created()), + ("atime", metadata.accessed()) + ]; + for timestamp_req in timestamps { + // if getting the timestamp was successful, add it + if let Ok(timestamp) = timestamp_req.1 { + // check if the duration since epoch is valid + // if it is, cast it as i64 seconds and add it to the dict + if let Ok(epoch_secs) = timestamp.duration_since(UNIX_EPOCH) { + times.insert(Value::String(timestamp_req.0.to_string()), Value::Int(epoch_secs.as_secs().cast_signed())); + } + } + }; + + // insert times section to the dictionary + return Value::Dictionary(alloc::sync::Arc::new(spin::RwLock::new(times))); +} + #[cfg(feature = "stdlib")] fn create_dict_from_file(path: &Path) -> AnyhowResult> { use alloc::format; @@ -137,12 +174,8 @@ fn create_dict_from_file(path: &Path) -> AnyhowResult> { Value::String(abs_path.to_string_lossy().to_string()), ); - // Times - if let Ok(modified) = metadata.modified() { - let dt: chrono::DateTime = modified.into(); - let formatted = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string(); - dict.insert("modified".to_string(), Value::String(formatted)); - } + // Add Time information + dict.insert("times".to_string(), get_times_dict(metadata)); Ok(dict) } @@ -166,7 +199,7 @@ mod tests { assert!(f.contains_key("owner")); assert!(f.contains_key("group")); assert!(f.contains_key("absolute_path")); - assert!(f.contains_key("modified")); + assert!(f.contains_key("times")); // Check absolute_path if let Value::String(abs) = &f["absolute_path"] { @@ -177,16 +210,16 @@ mod tests { } // Check modified time format - if let Value::String(mod_time) = &f["modified"] { - // Check format YYYY-MM-DD HH:MM:SS UTC - let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$").unwrap(); - assert!( - re.is_match(mod_time.as_bytes()), - "Timestamp format mismatch: {}", - mod_time - ); - } else { - panic!("modified is not a string"); - } + // if let Value::String(mod_time) = &f["modified"] { + // // Check format YYYY-MM-DD HH:MM:SS UTC + // let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$").unwrap(); + // assert!( + // re.is_match(mod_time.as_bytes()), + // "Timestamp format mismatch: {}", + // mod_time + // ); + // } else { + // panic!("modified is not a string"); + // } } } diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs index 502b20c92..de3991ac3 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs @@ -34,7 +34,6 @@ pub mod timestomp_impl; pub mod tmp_dir_impl; pub mod write_binary_impl; pub mod write_impl; -pub mod get_times_impl; #[derive(Debug, Default)] #[eldritch_library_impl(FileLibrary)] @@ -83,10 +82,6 @@ impl FileLibrary for StdFileLibrary { list_impl::list(path) } - fn get_times(&self, path: String) -> Result, String> { - get_times_impl::get_times(path) - } - fn list_named_pipes(&self, detailed: Option) -> Result { list_named_pipes_impl::list_named_pipes(detailed) } From 27f9d1f8e6dfc9ebb80b183f24da7d68aaa9ea4d Mon Sep 17 00:00:00 2001 From: meow Date: Thu, 9 Jul 2026 14:28:54 -0400 Subject: [PATCH 3/6] legacy support with stringified modification time, docs/name changes --- docs/_docs/user-guide/eldritch.md | 51 ++++++++++--------- .../eldritch-libfile/src/std/list_impl.rs | 40 +++++++++------ 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 6842bd87d..4da9b4e13 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -732,58 +732,61 @@ file.list("\\\\127.0.0.1\\c$\\Windows\\*.yml") # List files over UNC paths ``` Each file is represented by a Dict type. -Here is an example code snippet using **file.list**: +Here is an example code snippet along with it's output: ```python -print(file.list("/root/some_directory")) +print(file.list("/some_directory")) ``` ```json [ { - "absolute_path": "/root/some_directory", // when listing a directory, it will show the directory itself aswell + "absolute_path": "/some_directory", "file_name": "some_directory", "group": "root", + "modified": "2026-07-09 18:24:44 UTC", // modification time represented as a UTC string "owner": "root", "permissions": "40775", "size": 4096, - "times": { // the timestamps given are provided as seconds past the epoch (Jan 1 1970) - "atime": 1783528762, // accessed time - "crtime": 1783526598, // creation time - "ctime": 1783528762, // changed time (only available on UNIX systems) - "mtime": 1783528762 // modified time + "times": { // these are all the timestamps represented as UNIX epoch (seconds elapsed since Jan 1 1970) + "accessed": 1783621485, + "changed": 1783621484, // CHANGED TIME IS ONLY SUPPORTED IN UNIX SYSTEMS! + "created": 1783621124, + "modified": 1783621484 }, "type": "dir" }, { - "absolute_path": "/root/some_directory/some_file", - "file_name": "some_file", + "absolute_path": "/some_directory/some_other_directory", + "file_name": "some_other_directory", "group": "root", + "modified": "2026-07-09 18:18:47 UTC", "owner": "root", "permissions": "40775", "size": 4096, "times": { - "atime": 1783526640, - "crtime": 1783526598, - "ctime": 1783526598, - "mtime": 1783526598 + "accessed": 1783621155, + "changed": 1783621127, + "created": 1783621127, + "modified": 1783621127 }, - "type": "file" + "type": "dir" }, { - "absolute_path": "/root/some_directory/some_other_directory", - "file_name": "some_other_directory", + "absolute_path": "/some_directory/some_file", + "file_name": "some_file", "group": "root", + "modified": "2026-07-09 18:18:50 UTC", "owner": "root", - "permissions": "40775", - "size": 4096, + "permissions": "100664", + "size": 0, "times": { - "atime": 1783526640, - "crtime": 1783526598, - "ctime": 1783526598, - "mtime": 1783526598 + "accessed": 1783621130, + "changed": 1783621130, + "created": 1783621130, + "modified": 1783621130 }, - "type": "dir" + "type": "file" } ] ``` diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs index 5234f0afc..28fd753c0 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs @@ -82,14 +82,14 @@ fn get_times_dict(metadata: std::fs::Metadata) -> Value { // add changed time (it's already in epoch format) if we're in unix #[cfg(unix)] { use std::os::unix::fs::MetadataExt; - times.insert(Value::String("ctime".to_string()), Value::Int(metadata.ctime())); + times.insert(Value::String("changed".to_string()), Value::Int(metadata.ctime())); } // add time information let timestamps = [ - ("mtime", metadata.modified()), - ("crtime", metadata.created()), - ("atime", metadata.accessed()) + ("modified", metadata.modified()), + ("created", metadata.created()), + ("accessed", metadata.accessed()) ]; for timestamp_req in timestamps { // if getting the timestamp was successful, add it @@ -174,6 +174,13 @@ fn create_dict_from_file(path: &Path) -> AnyhowResult> { Value::String(abs_path.to_string_lossy().to_string()), ); + // Keep original modified time for backwards compatability prior to epoch addition + if let Ok(modified) = metadata.modified() { + let dt: chrono::DateTime = modified.into(); + let formatted = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string(); + dict.insert("modified".to_string(), Value::String(formatted)); + } + // Add Time information dict.insert("times".to_string(), get_times_dict(metadata)); @@ -200,6 +207,7 @@ mod tests { assert!(f.contains_key("group")); assert!(f.contains_key("absolute_path")); assert!(f.contains_key("times")); + assert!(f.contains_key("modified")); // Check absolute_path if let Value::String(abs) = &f["absolute_path"] { @@ -209,17 +217,17 @@ mod tests { panic!("absolute_path is not a string"); } - // Check modified time format - // if let Value::String(mod_time) = &f["modified"] { - // // Check format YYYY-MM-DD HH:MM:SS UTC - // let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$").unwrap(); - // assert!( - // re.is_match(mod_time.as_bytes()), - // "Timestamp format mismatch: {}", - // mod_time - // ); - // } else { - // panic!("modified is not a string"); - // } + //Check modified time format + if let Value::String(mod_time) = &f["modified"] { + // Check format YYYY-MM-DD HH:MM:SS UTC + let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC$").unwrap(); + assert!( + re.is_match(mod_time.as_bytes()), + "Timestamp format mismatch: {}", + mod_time + ); + } else { + panic!("modified is not a string"); + } } } From 8026b42539f2a1ac4ecbb4d963e224ad42cd5546 Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 12 Jul 2026 14:26:48 -0400 Subject: [PATCH 4/6] fixes --- docs/_docs/user-guide/eldritch.md | 68 +++++++++-------- .../stdlib/eldritch-libfile/src/fake.rs | 4 +- .../stdlib/eldritch-libfile/src/lib.rs | 4 +- .../eldritch-libfile/src/std/list_impl.rs | 76 ++++++++++++++----- .../stdlib/eldritch-libfile/src/std/mod.rs | 4 +- 5 files changed, 98 insertions(+), 58 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index 4da9b4e13..f6c89fe0e 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -720,9 +720,10 @@ The **file.is_file** method checks if a path exists and is a file. If it doesn't ### file.list -`file.list(path: str) -> List` +`file.list(path: str, dir_self: Optional = False) -> List` The **file.list** method returns a list of files at the specified path. The path is relative to your current working directory and can be traversed with `../`. +By default, when performing **file.list** against a directory, the directory itself will not be displayed, and only its actual contents will be displayed. To display the directory ALONGSIDE its contents, set `dir_self=True` This function also supports globbing with `*` for example: ```python @@ -731,62 +732,63 @@ file.list("/etc/*ssh*") # List the contents of all dirs that have `ssh` in the n file.list("\\\\127.0.0.1\\c$\\Windows\\*.yml") # List files over UNC paths ``` -Each file is represented by a Dict type. -Here is an example code snippet along with it's output: +Here is a code snippet example, along with its output. +Each file is returned as a Dict with their respective information. +In this case, `dir_self=True` is set, so `/tmp/some_dir` is shown alongside the contents. Setting `dir_self=False` or leaving it unset would not show it. ```python -print(file.list("/some_directory")) +print(file.list("/tmp/some_dir", dir_self=True)) ``` ```json [ { - "absolute_path": "/some_directory", - "file_name": "some_directory", + "absolute_path": "/tmp/some_dir", + "file_name": "some_dir", "group": "root", - "modified": "2026-07-09 18:24:44 UTC", // modification time represented as a UTC string + "modified": "2026-07-12 18:17:39 UTC", "owner": "root", "permissions": "40775", - "size": 4096, - "times": { // these are all the timestamps represented as UNIX epoch (seconds elapsed since Jan 1 1970) - "accessed": 1783621485, - "changed": 1783621484, // CHANGED TIME IS ONLY SUPPORTED IN UNIX SYSTEMS! - "created": 1783621124, - "modified": 1783621484 + "size": 80, + "times": { + "accessed": 1783880431, + "changed": 1783880259, // changed is Unix-only (ctime) + "created": 1783880259, + "modified": 1783880259 }, "type": "dir" }, { - "absolute_path": "/some_directory/some_other_directory", - "file_name": "some_other_directory", + "absolute_path": "/tmp/some_dir/some_file", + "file_name": "some_file", "group": "root", - "modified": "2026-07-09 18:18:47 UTC", + "modified": "2026-07-12 18:17:39 UTC", "owner": "root", - "permissions": "40775", - "size": 4096, + "permissions": "100664", + "size": 5, "times": { - "accessed": 1783621155, - "changed": 1783621127, - "created": 1783621127, - "modified": 1783621127 + "accessed": -2208988800, // negative epoch, represents 2208988800 seconds before Jan 1 1970 + "changed": 1783880431, + "created": 1783880259, + "modified": -2208988800 }, - "type": "dir" + "type": "file" }, { - "absolute_path": "/some_directory/some_file", - "file_name": "some_file", + "absolute_path": "/tmp/some_dir/some_other_dir", + "file_name": "some_other_dir", "group": "root", - "modified": "2026-07-09 18:18:50 UTC", + "modified": "1900-01-01 00:00:00 UTC", "owner": "root", - "permissions": "100664", - "size": 0, + "permissions": "40775", + "size": 40, "times": { - "accessed": 1783621130, - "changed": 1783621130, - "created": 1783621130, - "modified": 1783621130 + "accessed": 1783880259, + "changed": 1783880259, + "created": 1783880259, + "modified": 1783880259 }, - "type": "file" + "type": "dir" } ] ``` diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs index 2dc411480..0fdef946d 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs @@ -172,7 +172,7 @@ impl FileLibrary for FileLibraryFake { } } - fn list(&self, path: Option) -> Result>, String> { + fn list(&self, path: Option, dir_self: Option) -> Result>, String> { let path = path.unwrap_or_else(|| "/".to_string()); let mut root = self.root.lock(); let parts = Self::normalize_path(&path); @@ -486,7 +486,7 @@ mod tests { assert_eq!(file.read("/tmp/test.txt".into()).unwrap(), "hello"); // List - let items = file.list(Some("/home/user".into())).unwrap(); + let items = file.list(Some("/home/user".into()), None).unwrap(); assert!( items .iter() diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs index e5c69525c..0b190c694 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs @@ -142,6 +142,7 @@ pub trait FileLibrary { /// /// **Parameters** /// - `path` (`Option`): The directory path or glob pattern. Defaults to current working directory. + /// - `dir_self` (`Option`): Include self if file list target is a directory. Defaults to false. /// /// **Returns** /// - `List`: A list of dictionaries containing file details: @@ -152,11 +153,12 @@ pub trait FileLibrary { /// - `group` (`str`) /// - `permissions` (`str`) /// - `modified` (`str`) + /// - `times` (`dict`) /// - `type` (`str`: "File" or "Directory") /// /// **Errors** /// - Returns an error string if listing fails. - fn list(&self, path: Option) -> Result>, String>; + fn list(&self, path: Option, dir_self: Option) -> Result>, String>; #[eldritch_method] /// Lists all named pipes on the system. diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs index 28fd753c0..ce628624a 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs @@ -17,10 +17,14 @@ use std::fs; #[cfg(feature = "stdlib")] use std::path::Path; #[cfg(feature = "stdlib")] +use std::sync::Arc; +#[cfg(feature="stdlib")] +use spin::RwLock; +#[cfg(feature = "stdlib")] use std::time::UNIX_EPOCH; #[cfg(feature = "stdlib")] -pub fn list(path: Option) -> Result>, String> { +pub fn list(path: Option, dir_self: Option) -> Result>, String> { let path = path.unwrap_or_else(|| { ::std::env::current_dir() .map(|p| p.to_string_lossy().to_string()) @@ -32,12 +36,13 @@ pub fn list(path: Option) -> Result>, String } }) }); - list_impl(path).map_err(|e| e.to_string()) + list_impl(path, dir_self.unwrap_or(false)).map_err(|e| e.to_string()) } #[cfg(not(feature = "stdlib"))] pub fn list( _path: Option, + dir_self: Option ) -> Result< alloc::vec::Vec>, alloc::string::String, @@ -46,7 +51,7 @@ pub fn list( } #[cfg(feature = "stdlib")] -fn list_impl(path: String) -> AnyhowResult>> { +fn list_impl(path: String, dir_self: bool) -> AnyhowResult>> { use glob::glob; let mut final_res = Vec::new(); @@ -55,11 +60,14 @@ fn list_impl(path: String) -> AnyhowResult>> { for entry in glob(&path)? { match entry { Ok(path_buf) => { - // show information for the file - // if it's a directory, show information of the directory being listed from - final_res.push(create_dict_from_file(&path_buf)?); - - // if it is a directory, also add it's subcontents + // if we're not a directory (file), show self + // OR + // if we are a directory, only show self if the flag is set to true + if !path_buf.is_dir() || dir_self { + final_res.push(create_dict_from_file(&path_buf)?); + } + + // for dir, show subcontents if path_buf.is_dir() { for entry in fs::read_dir(&path_buf)? { let entry = entry?; @@ -70,12 +78,15 @@ fn list_impl(path: String) -> AnyhowResult>> { Err(e) => eprintln!("Glob error: {e:?}"), } } + + // sort by absolute_path + final_res.sort_by_key(|k| k.get("absolute_path").cloned()); Ok(final_res) } // get the timestamps of a metadata object and return it as a dictionary #[cfg(feature = "stdlib")] -fn get_times_dict(metadata: std::fs::Metadata) -> Value { +fn get_times_dict(metadata: std::fs::Metadata, mtime: std::io::Result) -> Value { // create dictionary for times data let mut times: BTreeMap = BTreeMap::new(); @@ -87,23 +98,26 @@ fn get_times_dict(metadata: std::fs::Metadata) -> Value { // add time information let timestamps = [ - ("modified", metadata.modified()), + ("modified", mtime), ("created", metadata.created()), ("accessed", metadata.accessed()) ]; for timestamp_req in timestamps { // if getting the timestamp was successful, add it if let Ok(timestamp) = timestamp_req.1 { - // check if the duration since epoch is valid - // if it is, cast it as i64 seconds and add it to the dict - if let Ok(epoch_secs) = timestamp.duration_since(UNIX_EPOCH) { - times.insert(Value::String(timestamp_req.0.to_string()), Value::Int(epoch_secs.as_secs().cast_signed())); - } + // convert timestamp to epoch + let secs = match timestamp.duration_since(UNIX_EPOCH) { + Ok(duration) => duration.as_secs() as i64, + Err(err) => -(err.duration().as_secs() as i64) + }; + + // add timestamp to dict + times.insert(Value::String(timestamp_req.0.to_string()), Value::Int(secs)); } }; // insert times section to the dictionary - return Value::Dictionary(alloc::sync::Arc::new(spin::RwLock::new(times))); + return Value::Dictionary(Arc::new(RwLock::new(times))); } #[cfg(feature = "stdlib")] @@ -174,15 +188,18 @@ fn create_dict_from_file(path: &Path) -> AnyhowResult> { Value::String(abs_path.to_string_lossy().to_string()), ); - // Keep original modified time for backwards compatability prior to epoch addition - if let Ok(modified) = metadata.modified() { + // cache modified time, then add it to both epoch and as a stringified version + let mtime = metadata.modified(); + + // turn modified time into a stringified version + if let Ok(modified) = mtime { let dt: chrono::DateTime = modified.into(); let formatted = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string(); dict.insert("modified".to_string(), Value::String(formatted)); } // Add Time information - dict.insert("times".to_string(), get_times_dict(metadata)); + dict.insert("times".to_string(), get_times_dict(metadata, mtime)); Ok(dict) } @@ -194,12 +211,20 @@ mod tests { use regex::bytes::Regex; use tempfile::NamedTempFile; + #[test] + fn test_list_includes_self_when_dir() { + let dir = tempfile::tempdir().unwrap(); + std::fs::write(dir.path().join("a.txt"), b"hi").unwrap(); + let files = list(Some(dir.path().to_string_lossy().to_string()), Some(true)).unwrap(); + assert!(files.len() >= 2); // self + child + } + #[test] fn test_list_owner_group() { let tmp = NamedTempFile::new().unwrap(); let path = tmp.path().to_string_lossy().to_string(); - let files = list(Some(path)).unwrap(); + let files = list(Some(path), None).unwrap(); assert_eq!(files.len(), 1); let f = &files[0]; @@ -207,6 +232,17 @@ mod tests { assert!(f.contains_key("group")); assert!(f.contains_key("absolute_path")); assert!(f.contains_key("times")); + // check times sub-dict + if let Value::Dictionary(d) = &f["times"] { + let inner = d.read(); + assert!(inner.contains_key(&Value::String("modified".into()))); + assert!(inner.contains_key(&Value::String("accessed".into()))); + assert!(inner.contains_key(&Value::String("created".into()))); + #[cfg(unix)] + assert!(inner.contains_key(&Value::String("changed".into()))); + } + + // check modified string assert!(f.contains_key("modified")); // Check absolute_path diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs index de3991ac3..6f0775028 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs @@ -78,8 +78,8 @@ impl FileLibrary for StdFileLibrary { is_file_impl::is_file(path) } - fn list(&self, path: Option) -> Result>, String> { - list_impl::list(path) + fn list(&self, path: Option, dir_self: Option) -> Result>, String> { + list_impl::list(path, dir_self) } fn list_named_pipes(&self, detailed: Option) -> Result { From 9b2712c162ffcaf7ce4293da988eb53540a6ae5e Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 12 Jul 2026 14:39:04 -0400 Subject: [PATCH 5/6] docs fixup --- docs/_docs/user-guide/eldritch.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/_docs/user-guide/eldritch.md b/docs/_docs/user-guide/eldritch.md index f6c89fe0e..024bd317e 100644 --- a/docs/_docs/user-guide/eldritch.md +++ b/docs/_docs/user-guide/eldritch.md @@ -723,7 +723,7 @@ The **file.is_file** method checks if a path exists and is a file. If it doesn't `file.list(path: str, dir_self: Optional = False) -> List` The **file.list** method returns a list of files at the specified path. The path is relative to your current working directory and can be traversed with `../`. -By default, when performing **file.list** against a directory, the directory itself will not be displayed, and only its actual contents will be displayed. To display the directory ALONGSIDE its contents, set `dir_self=True` +If `path` is a directory, `dir_self` denotes whether to show the information of `path` itself in the `List`. This field has no impact if `path` is not a directory. This function also supports globbing with `*` for example: ```python @@ -734,16 +734,17 @@ file.list("\\\\127.0.0.1\\c$\\Windows\\*.yml") # List files over UNC paths Here is a code snippet example, along with its output. Each file is returned as a Dict with their respective information. -In this case, `dir_self=True` is set, so `/tmp/some_dir` is shown alongside the contents. Setting `dir_self=False` or leaving it unset would not show it. ```python print(file.list("/tmp/some_dir", dir_self=True)) ``` +**NOTE:** On systems without a specific time field being tracked, the field is ommitted. This means, for example, unix systems with `noatime` set will not have an `accessed` field visible in the `times` sub-Dict. + ```json [ { - "absolute_path": "/tmp/some_dir", + "absolute_path": "/tmp/some_dir", // if dir_self were set to False or unset, this Dict would not be present here "file_name": "some_dir", "group": "root", "modified": "2026-07-12 18:17:39 UTC", From 9c8a89575bc608a282aa1db4eb00b4cfaa498c36 Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 19 Jul 2026 13:08:09 -0400 Subject: [PATCH 6/6] cargo fmt --- .../stdlib/eldritch-libfile/src/fake.rs | 6 ++- .../stdlib/eldritch-libfile/src/lib.rs | 6 ++- .../eldritch-libfile/src/std/list_impl.rs | 42 ++++++++++++------- .../stdlib/eldritch-libfile/src/std/mod.rs | 6 ++- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs index 0fdef946d..024fdf61e 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs @@ -172,7 +172,11 @@ impl FileLibrary for FileLibraryFake { } } - fn list(&self, path: Option, dir_self: Option) -> Result>, String> { + fn list( + &self, + path: Option, + dir_self: Option, + ) -> Result>, String> { let path = path.unwrap_or_else(|| "/".to_string()); let mut root = self.root.lock(); let parts = Self::normalize_path(&path); diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs index 0b190c694..a8eed4179 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs @@ -158,7 +158,11 @@ pub trait FileLibrary { /// /// **Errors** /// - Returns an error string if listing fails. - fn list(&self, path: Option, dir_self: Option) -> Result>, String>; + fn list( + &self, + path: Option, + dir_self: Option, + ) -> Result>, String>; #[eldritch_method] /// Lists all named pipes on the system. diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs index ce628624a..844d54e51 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/list_impl.rs @@ -13,18 +13,21 @@ use eldritch_core::Value; #[cfg(unix)] use nix::unistd::{Gid, Group, Uid, User}; #[cfg(feature = "stdlib")] +use spin::RwLock; +#[cfg(feature = "stdlib")] use std::fs; #[cfg(feature = "stdlib")] use std::path::Path; #[cfg(feature = "stdlib")] use std::sync::Arc; -#[cfg(feature="stdlib")] -use spin::RwLock; #[cfg(feature = "stdlib")] use std::time::UNIX_EPOCH; #[cfg(feature = "stdlib")] -pub fn list(path: Option, dir_self: Option) -> Result>, String> { +pub fn list( + path: Option, + dir_self: Option, +) -> Result>, String> { let path = path.unwrap_or_else(|| { ::std::env::current_dir() .map(|p| p.to_string_lossy().to_string()) @@ -42,7 +45,7 @@ pub fn list(path: Option, dir_self: Option) -> Result, - dir_self: Option + dir_self: Option, ) -> Result< alloc::vec::Vec>, alloc::string::String, @@ -66,7 +69,7 @@ fn list_impl(path: String, dir_self: bool) -> AnyhowResult AnyhowResult) -> Value { +fn get_times_dict( + metadata: std::fs::Metadata, + mtime: std::io::Result, +) -> Value { // create dictionary for times data let mut times: BTreeMap = BTreeMap::new(); // add changed time (it's already in epoch format) if we're in unix - #[cfg(unix)] { + #[cfg(unix)] + { use std::os::unix::fs::MetadataExt; - times.insert(Value::String("changed".to_string()), Value::Int(metadata.ctime())); + times.insert( + Value::String("changed".to_string()), + Value::Int(metadata.ctime()), + ); } // add time information let timestamps = [ ("modified", mtime), ("created", metadata.created()), - ("accessed", metadata.accessed()) + ("accessed", metadata.accessed()), ]; for timestamp_req in timestamps { // if getting the timestamp was successful, add it @@ -108,13 +118,13 @@ fn get_times_dict(metadata: std::fs::Metadata, mtime: std::io::Result duration.as_secs() as i64, - Err(err) => -(err.duration().as_secs() as i64) + Err(err) => -(err.duration().as_secs() as i64), }; // add timestamp to dict times.insert(Value::String(timestamp_req.0.to_string()), Value::Int(secs)); } - }; + } // insert times section to the dictionary return Value::Dictionary(Arc::new(RwLock::new(times))); @@ -233,14 +243,14 @@ mod tests { assert!(f.contains_key("absolute_path")); assert!(f.contains_key("times")); // check times sub-dict - if let Value::Dictionary(d) = &f["times"] { - let inner = d.read(); + if let Value::Dictionary(d) = &f["times"] { + let inner = d.read(); assert!(inner.contains_key(&Value::String("modified".into()))); assert!(inner.contains_key(&Value::String("accessed".into()))); assert!(inner.contains_key(&Value::String("created".into()))); - #[cfg(unix)] - assert!(inner.contains_key(&Value::String("changed".into()))); - } + #[cfg(unix)] + assert!(inner.contains_key(&Value::String("changed".into()))); + } // check modified string assert!(f.contains_key("modified")); diff --git a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs index 6f0775028..a945adea7 100644 --- a/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs +++ b/implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs @@ -78,7 +78,11 @@ impl FileLibrary for StdFileLibrary { is_file_impl::is_file(path) } - fn list(&self, path: Option, dir_self: Option) -> Result>, String> { + fn list( + &self, + path: Option, + dir_self: Option, + ) -> Result>, String> { list_impl::list(path, dir_self) }