use crate::{
error::{ClawDBError, ClawDBResult},
plugins::interface::{PluginCapability, PluginManifest},
};
pub struct PluginSandbox {
enabled: bool,
allowlist: Vec<PluginCapability>,
}
impl PluginSandbox {
pub fn new(enabled: bool) -> Self {
Self {
enabled,
allowlist: vec![
PluginCapability::ReadMemory,
PluginCapability::ModifySearchResults,
PluginCapability::HookTransactions,
PluginCapability::EmitEvents,
],
}
}
pub fn with_allowlist(enabled: bool, allowlist: Vec<PluginCapability>) -> Self {
Self { enabled, allowlist }
}
pub fn validate_capabilities(&self, manifest: &PluginManifest) -> ClawDBResult<()> {
if !self.enabled {
return Ok(());
}
for cap in &manifest.capabilities {
if !self.allowlist.contains(cap) {
return Err(ClawDBError::PluginCapabilityDenied {
plugin: manifest.name.clone(),
capability: format!("{cap:?}"),
});
}
}
self.reject_dangerous_combinations(&manifest.name, &manifest.capabilities)?;
Ok(())
}
fn reject_dangerous_combinations(
&self,
plugin_name: &str,
capabilities: &[PluginCapability],
) -> ClawDBResult<()> {
let has_write = capabilities.contains(&PluginCapability::WriteMemory);
let has_guard = capabilities.contains(&PluginCapability::AccessGuard);
let has_sync = capabilities.contains(&PluginCapability::AccessSync);
if has_write && has_guard {
return Err(ClawDBError::PluginCapabilityDenied {
plugin: plugin_name.to_string(),
capability: "WriteMemory+AccessGuard (dangerous combination)".to_string(),
});
}
if has_write && has_sync {
return Err(ClawDBError::PluginCapabilityDenied {
plugin: plugin_name.to_string(),
capability: "WriteMemory+AccessSync (dangerous combination)".to_string(),
});
}
Ok(())
}
pub fn assert_capability(
&self,
plugin_name: &str,
capability: &crate::plugins::traits::PluginCapability,
granted: &[crate::plugins::traits::PluginCapability],
) -> ClawDBResult<()> {
if !self.enabled {
return Ok(());
}
if !granted.contains(capability) {
return Err(ClawDBError::PluginCapabilityDenied {
plugin: plugin_name.to_string(),
capability: format!("{capability:?}"),
});
}
Ok(())
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
}