Everyone's talking about sandboxing their CLI tools and plugin systems. The default answer is "just fork a subprocess," as if that's the end of the story. It's not. Subprocess isolation is a joke if your threat model includes actual malicious code execution.
Let's break down the subprocess fantasy:
* You're still sharing the same kernel. A kernel LFE ruins your day.
* Resource limits (`ulimit`, `cgroups`) are coarse and often misconfigured.
* The attack surface is the entire syscall interface of the tool. `seccomp`-filtering a complex tool like `ffmpeg` or `imagemagick` is a career.
* You're paying full process lifecycle overhead for something that might just parse a JSON file.
WebAssembly, specifically the WASI preview2 target, offers a genuinely different trade-off. It's a capability-based, sandboxed runtime by *design*, not by bolted-on OS features. The threat model shifts from "contain the entire OS" to "deny all by default, grant explicit capabilities."
Consider a tool that needs to download and process user-submitted images. Classic subprocess model:
```bash
# The 'safe' way everyone suggests
subprocess.run(['convert', 'input.jpg', 'output.png'], timeout=5)
```
Now you're hoping `convert` has no CVEs today. Good luck.
With a WASM module, the runtime (like `wasmtime`) provides the capabilities. The module starts with nothing: no filesystem, no network, no clocks. You have to pass handles explicitly.
```rust
// Pseudocode for capability grant
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.preopened_dir(dir_cap, "./temp")? // Explicit directory access
.build();
store.data_mut().push(wasi);
// The module can ONLY read/write within './temp'
let instance = Linker::new(&engine).instantiate(&mut store, &module)?;
```
The advantages for tool isolation are concrete:
* **Memory Safety**: No spatial memory safety issues. A bug in the image library can't corrupt the host process.
* **Deterministic Resource Limits**: CPU fuel and memory limits are enforced by the runtime, predictably.
* **Minimal Attack Surface**: The "syscall" interface is the WASI API, which is vastly smaller and more auditable than Linux syscalls.
* **Instant Startup**: Orders of magnitude faster than forking a process, perfect for short-lived operations.
The catch? Obviously, you need to compile or port the tool to WASM. But for an internal toolchain or plugin system where you control the binaries, this is feasible. You're not running arbitrary binaries; you're running vetted, compiled WASM modules.
So before you reach for another `subprocess.Popen` and call it "security," ask: what's your actual threat model? If it's "I don't trust this code with my filesystem or CPU cycles," then WebAssembly provides a fundamentally more robust isolation primitive than a subprocess ever will.
- Ray
- Ray