From fab9efeac3708277bbdd1dbe410c60794f5d5d03 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Thu, 25 Sep 2025 12:59:25 +0300 Subject: [PATCH] feat: add trace saving and loading --- Cargo.lock | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/lib.rs | 45 +++++++++++++++++++++---- 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6626540..ba5218a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + [[package]] name = "captra" version = "0.1.0" @@ -50,6 +56,7 @@ dependencies = [ "glob", "serde", "serde_json", + "tempfile", "tracing", "tracing-subscriber", ] @@ -93,6 +100,16 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "eyre" version = "0.6.12" @@ -103,6 +120,24 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "gimli" version = "0.31.1" @@ -139,6 +174,12 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "log" version = "0.4.28" @@ -223,6 +264,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "regex-automata" version = "0.4.10" @@ -246,6 +293,19 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.20" @@ -321,6 +381,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -426,6 +499,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -498,3 +589,9 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/Cargo.toml b/Cargo.toml index 98f74e7..024c946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,9 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } [dev-dependencies] claims = "0.8" +tempfile = "3.23" [lints.clippy] pedantic = "warn" nursery = "warn" unwrap_used = "warn" -expect_used = "warn" diff --git a/src/lib.rs b/src/lib.rs index b969630..f716a9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ use color_eyre::eyre::Result; use glob::Pattern; use serde::{Deserialize, Serialize}; -use std::{fs::read_to_string, path::Path}; +use std::{ + fs::{self, read_to_string}, + path::Path, +}; use tracing::{Level, info}; #[derive(Debug, Serialize, Deserialize)] @@ -30,7 +33,7 @@ pub struct CapabilityManifest { // TODO: add signature } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct TraceEvent { pub seq: u64, pub event_type: String, @@ -99,6 +102,26 @@ impl HostState { pub fn finalize_trace(&self) -> String { serde_json::to_string_pretty(&self.trace).unwrap_or_else(|_| "[]".into()) } + + /// Save the current trace to a file as pretty JSON. + /// # Errors + /// If file write fails (e.g., I/O error) or JSON serialization fails. + pub fn save_trace>(&self, path: P) -> Result<()> { + let json_str = serde_json::to_string_pretty(&self.trace)?; + fs::write(path, json_str)?; + Ok(()) + } + + /// Load a trace from a JSON file to Vec. + /// + /// # Errors + /// + /// If file read fails (e.g., I/O error) or JSON parsing fails. + pub fn load_trace>(path: P) -> Result> { + let json_str = fs::read_to_string(path)?; + let trace = serde_json::from_str(&json_str)?; + Ok(trace) + } } /// Loads a capability manifest from a JSON file. @@ -120,6 +143,7 @@ pub fn init_tracing() { mod tests { use super::*; use claims::{assert_ok, assert_some}; + use tempfile::tempdir; #[test] fn tracing_init() { @@ -151,7 +175,6 @@ mod tests { // allowed - expect a tracing event let out1 = host.execute_plugin("./workspace/config.toml"); assert!(out1); - // denied - event + entry let out2 = host.execute_plugin("/etc/passwd"); assert!(!out2); @@ -169,8 +192,18 @@ mod tests { assert_eq!(trace2.input, "/etc/passwd"); assert!(!trace2.outcome); - let json = host.finalize_trace(); - let parsed = assert_ok!(serde_json::from_str::>(&json)); - assert_eq!(parsed.len(), 2); + let tmp_dir = tempdir().expect("Temp dir failed"); + let tmp_path = tmp_dir.as_ref().join("trace.json"); + + assert_ok!(host.save_trace(&tmp_path)); + + let loaded_trace = assert_ok!(HostState::load_trace(tmp_path)); + assert_eq!(loaded_trace.len(), 2); + + let loaded_trace1 = assert_some!(loaded_trace.first()); + assert_eq!(trace1, loaded_trace1); + + let loaded_trace2 = assert_some!(loaded_trace.get(1)); + assert_eq!(trace2, loaded_trace2); } }