mirror of
https://github.com/kristoferssolo/captra.git
synced 2025-12-20 11:04:39 +00:00
feat: add trace saving and loading
This commit is contained in:
parent
45bfa3f50e
commit
fab9efeac3
97
Cargo.lock
generated
97
Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
45
src/lib.rs
45
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<P: AsRef<Path>>(&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<TraceEvent>.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If file read fails (e.g., I/O error) or JSON parsing fails.
|
||||
pub fn load_trace<P: AsRef<Path>>(path: P) -> Result<Vec<TraceEvent>> {
|
||||
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::<Vec<TraceEvent>>(&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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user