jsdet-core 0.1.0

Core WASM-sandboxed JavaScript detonation engine
Documentation
/// Configuration for a single sandbox execution.
///
/// Sensible defaults are provided. Zero config for consumers who just want
/// `execute(scripts)`. Full control for experts.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SandboxConfig {
    /// Maximum wall-clock execution time in milliseconds.
    /// Default: 200ms. Enough for most phishing kits.
    /// Set to 5000ms for complex extension analysis.
    pub timeout_ms: u64,

    /// Maximum WASM linear memory in bytes.
    /// Default: 16MB. `QuickJS` needs ~4MB for bootstrap.
    pub max_memory_bytes: usize,

    /// Maximum fuel (WASM instruction count).
    /// Default: `100_000_000` (~100ms of execution on modern hardware).
    /// 0 = unlimited (use timeout only).
    pub max_fuel: u64,

    /// Maximum observations before the sandbox stops recording.
    /// Prevents memory exhaustion from observation floods.
    /// Default: `10_000`.
    pub max_observations: usize,

    /// Maximum number of scripts to execute per sandbox run.
    /// Default: 100.
    pub max_scripts: usize,

    /// Maximum size of a single script in bytes.
    /// Default: 1MB.
    pub max_script_bytes: usize,

    /// Maximum combined size of all scripts in bytes.
    /// Default: 5MB.
    pub max_total_script_bytes: usize,

    /// Whether to immediately drain all pending timers after script execution.
    /// When true, setTimeout/setInterval callbacks fire synchronously.
    /// Default: true (for detonation — you want to trigger delayed payloads).
    pub drain_timers: bool,

    /// Maximum timer callbacks to drain per execution.
    /// Default: 50.
    pub max_timer_drains: usize,

    /// Whether to allow nested WASM instantiation.
    /// When true, JS `new WebAssembly.Module()` creates a real nested WASM instance.
    /// When false, it returns a stub that records the attempt but doesn't execute.
    /// Default: true.
    pub allow_nested_wasm: bool,

    /// Maximum linear memory for nested WASM instances in bytes.
    /// Default: 4MB.
    pub nested_wasm_max_memory: usize,

    /// Maximum fuel for nested WASM instances.
    /// Default: `10_000_000`.
    pub nested_wasm_max_fuel: u64,
}

impl Default for SandboxConfig {
    fn default() -> Self {
        Self {
            timeout_ms: 200,
            max_memory_bytes: 16 * 1024 * 1024,
            max_fuel: 100_000_000,
            max_observations: 10_000,
            max_scripts: 100,
            max_script_bytes: 1024 * 1024,
            max_total_script_bytes: 5 * 1024 * 1024,
            drain_timers: true,
            max_timer_drains: 50,
            // Secure by default — nested WASM disabled. Enable explicitly for extension analysis.
            allow_nested_wasm: false,
            nested_wasm_max_memory: 4 * 1024 * 1024,
            nested_wasm_max_fuel: 10_000_000,
        }
    }
}

impl SandboxConfig {
    /// Config tuned for fast URL detonation (Sear).
    /// Low timeouts, drain timers, nested WASM disabled.
    #[must_use]
    pub fn detonation() -> Self {
        Self {
            timeout_ms: 200,
            max_fuel: 50_000_000,
            drain_timers: true,
            allow_nested_wasm: false,
            ..Self::default()
        }
    }

    /// Config tuned for deep extension analysis (Soleno).
    /// Higher timeouts, nested WASM enabled, more observations.
    #[must_use]
    pub fn extension_analysis() -> Self {
        Self {
            timeout_ms: 5000,
            max_fuel: 500_000_000,
            max_observations: 50_000,
            max_scripts: 500,
            drain_timers: true,
            allow_nested_wasm: true,
            ..Self::default()
        }
    }

    /// Config for interactive research.
    /// No fuel limit, high timeouts, everything enabled.
    #[must_use]
    pub fn research() -> Self {
        Self {
            timeout_ms: 30_000,
            max_fuel: 0, // unlimited
            max_observations: 1_000_000,
            max_scripts: 10_000,
            max_memory_bytes: 256 * 1024 * 1024,
            drain_timers: false, // researcher controls timer advancement
            allow_nested_wasm: true,
            nested_wasm_max_memory: 64 * 1024 * 1024,
            nested_wasm_max_fuel: 100_000_000,
            ..Self::default()
        }
    }
}