mirror of
https://github.com/kristoferssolo/captra.git
synced 2026-02-25 04:58:15 +00:00
feat: add plugin execution
This commit is contained in:
65
src/lib.rs
65
src/lib.rs
@@ -1,18 +1,18 @@
|
||||
use std::fs::read_to_string;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use glob::Pattern;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs::read_to_string, path::Path};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct FsCapability {
|
||||
pub read: Vec<String>, // Glob patter for read
|
||||
pub write: Vec<String>, // Stub for now
|
||||
pub read: Option<Vec<String>>, // Glob patter for read
|
||||
pub write: Option<Vec<String>>, // Stub for now
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Capability {
|
||||
Fs(FsCapability),
|
||||
// TODO: add Net, Cpu
|
||||
// TODO: add Net, Cpu, etc
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -29,6 +29,44 @@ pub struct CapabilityManifest {
|
||||
// TODO: add signature
|
||||
}
|
||||
|
||||
pub struct HostState {
|
||||
pub manifest: CapabilityManifest,
|
||||
}
|
||||
|
||||
impl HostState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(manifest: CapabilityManifest) -> Self {
|
||||
Self { manifest }
|
||||
}
|
||||
|
||||
/// Simulate "plugin execution": check if path is allowed via FS read cap.
|
||||
/// Returns `true` if allowed, `false` if denied.
|
||||
#[must_use]
|
||||
pub fn execute_plugin<P: AsRef<Path>>(&self, path: P) -> bool {
|
||||
let Some(fs_cap) = &self.manifest.capabilities.fs else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let Some(read_patterns) = &fs_cap.read else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let path_str = path.as_ref().to_string_lossy();
|
||||
|
||||
read_patterns.iter().any(|pattern| {
|
||||
Pattern::new(pattern)
|
||||
.map(|p| p.matches(&path_str))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a capability manifest from a JSON file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - bubbles up `std::fs::read_to_string` and `serde_json::from_str` errors;
|
||||
pub fn load_manifest(path: &str) -> Result<CapabilityManifest> {
|
||||
let json_str = read_to_string(path)?;
|
||||
let manifest = serde_json::from_str(&json_str)?;
|
||||
@@ -49,7 +87,20 @@ mod tests {
|
||||
|
||||
let caps = manifest.capabilities;
|
||||
let fs_cap = assert_some!(caps.fs);
|
||||
assert_eq!(fs_cap.read, vec!["./workspace/*"]);
|
||||
assert!(fs_cap.write.is_empty());
|
||||
let read_patterns = assert_some!(fs_cap.read);
|
||||
assert_eq!(read_patterns, vec!["./workspace/*"]);
|
||||
let write_patterns = assert_some!(fs_cap.write);
|
||||
assert!(write_patterns.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_enforcement() {
|
||||
let manifest = load_manifest("examples/manifest.json").expect("Load failed");
|
||||
let host = HostState::new(manifest);
|
||||
|
||||
// Allowed: matches ./workspace/*
|
||||
assert!(host.execute_plugin("./workspace/config.toml"));
|
||||
// Disallowed: outside pattern
|
||||
assert!(!host.execute_plugin("/etc/passwd"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user