use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::time::Duration;
use crate::host::buildtime::capabilities::CapabilitySet;
#[derive(Debug, Clone, PartialEq)]
pub enum SandboxValue {
Null,
Undefined,
Bool(bool),
Number(f64),
BigInt(i64),
String(String),
Array(Vec<SandboxValue>),
Object(BTreeMap<String, SandboxValue>),
SourceCode(String),
}
impl SandboxValue {
#[must_use]
pub fn is_nullish(&self) -> bool {
matches!(self, Self::Null | Self::Undefined)
}
#[must_use]
pub fn kind_name(&self) -> &'static str {
match self {
Self::Null => "null",
Self::Undefined => "undefined",
Self::Bool(_) => "boolean",
Self::Number(_) => "number",
Self::BigInt(_) => "bigint",
Self::String(_) => "string",
Self::Array(_) => "array",
Self::Object(_) => "object",
Self::SourceCode(_) => "source-code",
}
}
}
#[derive(Debug, Clone)]
pub struct SandboxOptions {
pub capabilities: CapabilitySet,
pub timeout: Duration,
pub max_heap: usize,
pub source_file: PathBuf,
pub source_line: u32,
pub source_column: u32,
}
impl SandboxOptions {
#[must_use]
pub fn new(source_file: PathBuf) -> Self {
Self {
capabilities: CapabilitySet::default(),
timeout: Duration::from_secs(5),
max_heap: 256 * 1024 * 1024,
source_file,
source_line: 1,
source_column: 1,
}
}
}
#[derive(Debug, Clone)]
pub struct EvalResult {
pub value: SandboxValue,
pub dependencies: Vec<PathBuf>,
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum SandboxError {
#[error("script timed out after {duration:?}")]
Timeout { duration: Duration },
#[error("script exceeded heap limit of {limit} bytes")]
OutOfMemory { limit: usize },
#[error("script tried to read disallowed path {}", .path.display())]
UnauthorizedRead { path: PathBuf },
#[error("script tried to write disallowed path {}", .path.display())]
UnauthorizedWrite { path: PathBuf },
#[error("script tried to read disallowed env var {var}")]
UnauthorizedEnv { var: String },
#[error("script tried to make network request to {url} (network capability is off)")]
UnauthorizedNetwork { url: String },
#[error("script returned an unsupported value: {kind}")]
UnserializableResult { kind: String },
#[error("script threw: {message}")]
Threw { message: String, stack: String },
#[error("backend error: {0}")]
Backend(String),
}
pub trait BuildtimeSandbox: Send + Sync {
fn name(&self) -> &'static str;
fn evaluate(
&self,
source: &str,
origin: &Path,
options: &SandboxOptions,
) -> Result<EvalResult, SandboxError>;
}