use async_trait::async_trait;
use crate::{
BackendCapabilities, EnvironmentPolicy, ExecutionError, ExecutionLanguage, ExecutionPayload,
ExecutionRequest, ExecutionResult, FilesystemPolicy, GuestModuleFormat, NetworkPolicy,
SandboxPolicy,
};
#[async_trait]
pub trait CodeExecutor: Send + Sync {
fn name(&self) -> &str;
fn capabilities(&self) -> BackendCapabilities;
fn supports_language(&self, lang: &ExecutionLanguage) -> bool;
async fn execute(&self, request: ExecutionRequest) -> Result<ExecutionResult, ExecutionError>;
async fn start(&self) -> Result<(), ExecutionError> {
Ok(())
}
async fn stop(&self) -> Result<(), ExecutionError> {
Ok(())
}
async fn restart(&self) -> Result<(), ExecutionError> {
self.stop().await?;
self.start().await
}
async fn is_running(&self) -> bool {
true
}
}
pub fn validate_policy(
capabilities: &BackendCapabilities,
policy: &SandboxPolicy,
) -> Result<(), ExecutionError> {
if matches!(policy.network, NetworkPolicy::Disabled) && !capabilities.enforce_network_policy {
return Err(ExecutionError::UnsupportedPolicy(
"backend cannot enforce network restrictions".to_string(),
));
}
if !matches!(policy.filesystem, FilesystemPolicy::None)
&& !capabilities.enforce_filesystem_policy
{
return Err(ExecutionError::UnsupportedPolicy(
"backend cannot enforce filesystem restrictions".to_string(),
));
}
if !matches!(policy.environment, EnvironmentPolicy::None)
&& !capabilities.enforce_environment_policy
{
return Err(ExecutionError::UnsupportedPolicy(
"backend cannot enforce environment variable restrictions".to_string(),
));
}
if !capabilities.enforce_timeout {
return Err(ExecutionError::UnsupportedPolicy(
"backend cannot enforce execution timeouts".to_string(),
));
}
Ok(())
}
pub fn validate_request(
capabilities: &BackendCapabilities,
supported_languages: &[ExecutionLanguage],
request: &ExecutionRequest,
) -> Result<(), ExecutionError> {
if !supported_languages.contains(&request.language) {
return Err(ExecutionError::UnsupportedLanguage(format!("{}", request.language)));
}
match (&request.language, &request.payload) {
(lang, ExecutionPayload::GuestModule { format, .. }) => match format {
GuestModuleFormat::Wasm if *lang != ExecutionLanguage::Wasm => {
return Err(ExecutionError::InvalidRequest(format!(
"GuestModule(Wasm) payload requires Wasm language, got {lang}"
)));
}
_ => {}
},
(ExecutionLanguage::Wasm, ExecutionPayload::Source { .. }) => {
return Err(ExecutionError::InvalidRequest(
"Wasm language requires a GuestModule payload, not Source".to_string(),
));
}
_ => {}
}
validate_policy(capabilities, &request.sandbox)?;
Ok(())
}