wasm-sandbox 0.4.1

A secure WebAssembly sandbox with dead-simple ease of use, progressive complexity APIs, and comprehensive safety controls
Documentation
// CLI Tool Wrapper Template
// This template is used to wrap a CLI tool 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 TOOL_EXECUTABLE: &str = "{{tool_executable}}";
const INTERACTIVE: bool = {{interactive}};

// Global state
static mut TOOL_INSTANCE: Option<Arc<Mutex<ToolInstance>>> = None;

struct ToolInstance {
    running: bool,
    process: Option<Child>,
    output: Vec<u8>,
}

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

// Run the tool with arguments
#[no_mangle]
pub extern "C" fn run(args_ptr: *const u8, args_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)
    };
    
    let args_str = match std::str::from_utf8(args_bytes) {
        Ok(s) => s,
        Err(_) => return false,
    };
    
    // Parse arguments from JSON
    let args: Vec<String> = match serde_json::from_str(args_str) {
        Ok(a) => a,
        Err(_) => return false,
    };
    
    // Get tool instance
    let instance = unsafe { TOOL_INSTANCE.as_ref().unwrap().clone() };
    let mut instance = instance.lock().unwrap();
    
    if instance.running {
        // Tool is already running
        return false;
    }
    
    // Build command to start the tool
    let mut command = Command::new(TOOL_EXECUTABLE);
    for arg in args {
        command.arg(arg);
    }
    
    // Configure stdio
    command.stdout(Stdio::piped());
    command.stderr(Stdio::piped());
    
    if INTERACTIVE {
        command.stdin(Stdio::piped());
    }
    
    // Start the tool
    match command.spawn() {
        Ok(mut child) => {
            instance.running = true;
            
            if !INTERACTIVE {
                // For non-interactive tools, capture output
                let stdout = child.stdout.take().expect("Failed to capture stdout");
                
                let instance_clone = unsafe { SERVER_INSTANCE.as_ref().unwrap().clone() };
                
                thread::spawn(move || {
                    let mut buffer = Vec::new();
                    let mut stdout_reader = std::io::BufReader::new(stdout);
                    
                    if let Err(_) = stdout_reader.read_to_end(&mut buffer) {
                        return;
                    }
                    
                    let mut instance = instance_clone.lock().unwrap();
                    instance.output = buffer;
                });
            }
            
            instance.process = Some(child);
            true
        },
        Err(_) => false,
    }
}

// Send input to the tool (for interactive tools)
#[no_mangle]
pub extern "C" fn send_input(input_ptr: *const u8, input_len: usize) -> bool {
    if !INTERACTIVE {
        return false;
    }
    
    // Convert input_ptr to a string
    let input_bytes = unsafe {
        if input_ptr.is_null() {
            return false;
        }
        std::slice::from_raw_parts(input_ptr, input_len)
    };
    
    // Get tool instance
    let instance = unsafe { TOOL_INSTANCE.as_ref().unwrap().clone() };
    let mut instance = instance.lock().unwrap();
    
    if !instance.running || instance.process.is_none() {
        return false;
    }
    
    // Get stdin of the process
    if let Some(child) = &mut instance.process {
        if let Some(stdin) = child.stdin.as_mut() {
            if let Err(_) = stdin.write_all(input_bytes) {
                return false;
            }
            if let Err(_) = stdin.flush() {
                return false;
            }
            return true;
        }
    }
    
    false
}

// Get output from the tool
#[no_mangle]
pub extern "C" fn get_output() -> *const u8 {
    let instance = unsafe { TOOL_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 { TOOL_INSTANCE.as_ref().unwrap().clone() };
    let instance = instance.lock().unwrap();
    instance.output.len()
}

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

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

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