wasm-sandbox 0.4.1

A secure WebAssembly sandbox with dead-simple ease of use, progressive complexity APIs, and comprehensive safety controls
Documentation
// Generic Program Wrapper Template
// This template is used to wrap a generic program in a WebAssembly module

use std::sync::{Arc, Mutex};
use std::process::{Command, Stdio, Child};
use std::io::{Read, Write};
use std::thread;

// Configuration
const EXECUTABLE: &str = "{{executable}}";

// Global state
static mut PROGRAM_INSTANCE: Option<Arc<Mutex<ProgramInstance>>> = None;

struct ProgramInstance {
    running: bool,
    process: Option<Child>,
    exit_code: Option<i32>,
    output: Vec<u8>,
}

// Initialize the module
#[no_mangle]
pub extern "C" fn _initialize() {
    unsafe {
        PROGRAM_INSTANCE = Some(Arc::new(Mutex::new(ProgramInstance {
            running: false,
            process: None,
            exit_code: None,
            output: Vec::new(),
        })));
    }
}

// Run the program with arguments and environment variables
#[no_mangle]
pub extern "C" fn run(
    args_ptr: *const u8, 
    args_len: usize, 
    env_ptr: *const u8, 
    env_len: usize,
    working_dir_ptr: *const u8,
    working_dir_len: usize,
) -> bool {
    // Convert args_ptr to a string
    let args_bytes = unsafe {
        if args_ptr.is_null() {
            return false;
        }
        std::slice::from_raw_parts(args_ptr, args_len)
    };
    
    // Parse arguments from JSON
    let args: Vec<String> = match serde_json::from_str(std::str::from_utf8(args_bytes).unwrap_or("[]")) {
        Ok(a) => a,
        Err(_) => Vec::new(),
    };
    
    // Convert env_ptr to a string
    let env_bytes = unsafe {
        if env_ptr.is_null() {
            &[] as &[u8]
        } else {
            std::slice::from_raw_parts(env_ptr, env_len)
        }
    };
    
    // Parse environment variables from JSON
    let env_vars: std::collections::HashMap<String, String> = 
        match serde_json::from_str(std::str::from_utf8(env_bytes).unwrap_or("{}")) {
            Ok(e) => e,
            Err(_) => std::collections::HashMap::new(),
        };
        
    // Convert working_dir_ptr to a string
    let working_dir = if working_dir_ptr.is_null() || working_dir_len == 0 {
        None
    } else {
        let dir_bytes = unsafe {
            std::slice::from_raw_parts(working_dir_ptr, working_dir_len)
        };
        
        match std::str::from_utf8(dir_bytes) {
            Ok(s) if !s.is_empty() => Some(s),
            _ => None,
        }
    };
    
    // Get program instance
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let mut instance = instance.lock().unwrap();
    
    if instance.running {
        // Program is already running
        return false;
    }
    
    // Build command to start the program
    let mut command = Command::new(EXECUTABLE);
    
    // Add arguments
    for arg in args {
        command.arg(arg);
    }
    
    // Add environment variables
    for (key, value) in env_vars {
        command.env(key, value);
    }
    
    // Set working directory
    if let Some(dir) = working_dir {
        command.current_dir(dir);
    }
    
    // Configure stdio
    command.stdout(Stdio::piped());
    command.stderr(Stdio::piped());
    
    // Start the program
    match command.spawn() {
        Ok(mut child) => {
            instance.running = true;
            
            // Capture output
            let stdout = child.stdout.take().expect("Failed to capture stdout");
            let stderr = child.stderr.take().expect("Failed to capture stderr");
            
            let instance_clone = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
            
            thread::spawn(move || {
                let mut buffer = Vec::new();
                let mut stdout_reader = std::io::BufReader::new(stdout);
                let mut stderr_reader = std::io::BufReader::new(stderr);
                
                // Read stdout
                let _ = stdout_reader.read_to_end(&mut buffer);
                
                // Read stderr and append to buffer
                let mut stderr_buffer = Vec::new();
                let _ = stderr_reader.read_to_end(&mut stderr_buffer);
                buffer.extend_from_slice(&stderr_buffer);
                
                let mut instance = instance_clone.lock().unwrap();
                instance.output = buffer;
            });
            
            instance.process = Some(child);
            true
        },
        Err(_) => false,
    }
}

// Wait for the program to exit
#[no_mangle]
pub extern "C" fn wait() -> i32 {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let mut instance = instance.lock().unwrap();
    
    if !instance.running || instance.process.is_none() {
        return -1;
    }
    
    if let Some(exit_code) = instance.exit_code {
        instance.running = false;
        return exit_code;
    }
    
    // Wait for the program to exit
    if let Some(child) = &mut instance.process {
        match child.wait() {
            Ok(status) => {
                instance.running = false;
                let code = status.code().unwrap_or(-1);
                instance.exit_code = Some(code);
                code
            },
            Err(_) => -1,
        }
    } else {
        -1
    }
}

// Get output from the program
#[no_mangle]
pub extern "C" fn get_output() -> *const u8 {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let instance = instance.lock().unwrap();
    
    if instance.output.is_empty() {
        return std::ptr::null();
    }
    
    let ptr = instance.output.as_ptr();
    std::mem::forget(instance.output.clone());
    ptr
}

// Get output length
#[no_mangle]
pub extern "C" fn get_output_len() -> usize {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let instance = instance.lock().unwrap();
    instance.output.len()
}

// Stop the program
#[no_mangle]
pub extern "C" fn stop() {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let mut instance = instance.lock().unwrap();
    
    if !instance.running {
        return;
    }
    
    // Stop the program
    if let Some(mut child) = instance.process.take() {
        let _ = child.kill();
        let _ = child.wait();
    }
    
    instance.running = false;
}

// Check if the program is running
#[no_mangle]
pub extern "C" fn is_running() -> bool {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let instance = instance.lock().unwrap();
    instance.running
}

// Get exit code
#[no_mangle]
pub extern "C" fn get_exit_code() -> i32 {
    let instance = unsafe { PROGRAM_INSTANCE.as_ref().unwrap().clone() };
    let instance = instance.lock().unwrap();
    instance.exit_code.unwrap_or(-1)
}

// Main function
fn main() {
    // Initialize
    _initialize();
    
    // The actual logic will be handled by the host through function calls
    println!("Program wrapper initialized");
}