jsdet-core 0.1.0

Core WASM-sandboxed JavaScript detonation engine
Documentation
use std::sync::Arc;

use crate::{Bridge, CompiledModule, ExecutionResult, Observation, Result, SandboxConfig};

/// Reusable sandbox wrapper that keeps a loaded script set and re-executes it
/// with appended probe code.
///
/// This preserves the public API expected by Soleno and jsdet consumers while
/// keeping the execution model explicit and deterministic.
pub struct PersistentSandbox {
    module: Arc<CompiledModule>,
    bridge: Arc<dyn Bridge>,
    config: SandboxConfig,
    loaded_scripts: Vec<String>,
}

impl PersistentSandbox {
    /// Create a new reusable sandbox.
    pub fn new(
        module: &CompiledModule,
        bridge: Arc<dyn Bridge>,
        config: &SandboxConfig,
    ) -> Result<Self> {
        let serialized = module.serialize()?;
        let module = CompiledModule::load_cached(&serialized)?;
        Ok(Self {
            module: Arc::new(module),
            bridge,
            config: config.clone(),
            loaded_scripts: Vec::new(),
        })
    }

    /// Load the baseline scripts that should run before every probe.
    pub fn load(&mut self, scripts: &[String]) -> Result<()> {
        self.loaded_scripts = scripts.to_vec();
        let _ = self.execute(&[])?;
        Ok(())
    }

    /// Execute probe code against the currently loaded script set.
    #[must_use]
    pub fn eval_only(&mut self, script: &str) -> Vec<Observation> {
        self.execute(&[script.to_string()])
            .map(|result| result.observations)
            .unwrap_or_default()
    }

    /// Return the currently configured store fuel.
    pub fn store_fuel(&self) -> Result<u64> {
        Ok(self.config.max_fuel)
    }

    /// Update the configured store fuel for future executions.
    pub fn set_store_fuel(&mut self, fuel: u64) {
        self.config.max_fuel = fuel;
    }

    fn execute(&self, extra_scripts: &[String]) -> Result<ExecutionResult> {
        let mut scripts = self.loaded_scripts.clone();
        scripts.extend(extra_scripts.iter().cloned());
        self.module
            .execute(&scripts, Arc::clone(&self.bridge), &self.config)
    }
}