Hardened WASM Sandbox Executor (CONCEPT:OS-5.4)¶
Overview¶
Rather than executing raw local subprocesses or shell commands which pose severe prompt and command injection risks, the kernel executes micro-agents and untrusted utility tools inside an isolated WebAssembly (WASM) Sandbox.
By utilizing the standard wasmtime engine, the sandbox guarantees memory safety, execution budget enforcement, and microsecond-level isolation.
Architecture¶
The WASM executor operates in two modes:
Native Mode (wasmtime installed)¶
┌─────────────────────────────────────────────┐
│ WasmAgentRunner │
│ │
JSON input ──►│ 1. Configure wasmtime Engine (Cranelift) │
│ 2. Store + Module compilation │
│ 3. Linker → Instantiate module │
│ 4. Allocate linear memory for input │
│ 5. Write JSON bytes to WASM memory │
│ 6. Call run(ptr, len) → output_ptr │
│ 7. Read output from WASM memory │
│ 8. Parse JSON output │──► JSON output
└─────────────────────────────────────────────┘
Emulation/Fallback Mode (no wasmtime)¶
JSON input ──► Action Router ──► safe_eval_math() ──► JSON output
│
├── calculate_fees (base + state + expedited)
├── draft_calculations (shares × par value)
└── expand_template ({{variable}} substitution)
Gas Model¶
WASM execution is constrained by the Cranelift compiler's native instruction metering:
| Resource | Default Limit | Rationale |
|---|---|---|
| Memory Pages | 16 pages (1 MB) | Prevents OOM crashes on the host |
| Linear Memory | Bounded by limit_memory_pages × 64KB |
WASM spec: 1 page = 64KB |
| Compilation Strategy | Cranelift (AOT) | Sub-millisecond cold starts |
| Output Buffer | 4096 bytes | Prevents unbounded output extraction |
Future enhancement: Explicit fuel metering (wasmtime's consume_fuel API) to enforce per-invocation CPU budgets.
Memory Model¶
WASM provides linear memory isolation — the sandbox cannot access host memory, filesystem, or network:
Host Process Memory
├── Python heap (unreachable from WASM)
├── wasmtime Engine state
└── WASM Store
└── Linear Memory (sandboxed)
├── [0..input_ptr]: Reserved
├── [input_ptr..input_ptr+len]: Input JSON bytes
├── [output_ptr..output_ptr+4096]: Output JSON bytes
└── [4096+..]: Available for module-internal use
Memory Safety Guarantees¶
- No pointer escapes — WASM pointers are offsets into linear memory, not host addresses
- Bounds checking — Every load/store is validated against memory size
- Stack isolation — WASM operand stack is separate from host call stack
- No system calls — Unless explicitly linked via the wasmtime
Linker
Syscall Policy¶
The default WasmAgentRunner linker imports zero host functions, creating a fully isolated sandbox:
| Capability | Default | Notes |
|---|---|---|
| Filesystem access | ❌ Denied | No WASI filesystem imports |
| Network access | ❌ Denied | No WASI socket imports |
| Environment variables | ❌ Denied | No environ_get import |
| Clock/time | ❌ Denied | No clock_time_get import |
| Random | ❌ Denied | No random_get import |
| stdout/stderr | ❌ Denied | No fd_write import |
To enable selective capabilities (e.g., for WASI-compatible modules), extend the linker:
runner = WasmAgentRunner(limit_memory_pages=32)
# Future: runner.enable_wasi(fs=False, net=False, clock=True)
Safe Math Evaluation¶
The fallback mode uses safe_eval_math() instead of Python's eval():
- AST Whitelisting — Parses expression into AST, only allows
Constant,BinOp,UnaryOp,Name - Operator Restriction — Only
+,-,*,/, unary-, unary+ - Variable Binding — Only pre-declared numeric variables from the input dict
- No Imports — No access to
__builtins__,os,sys, or any module - Type Enforcement — Only
intandfloatvalues permitted in expressions
# Safe:
safe_eval_math("base_fee + state_fee * 1.5", {"base_fee": 100, "state_fee": 50})
# → 175.0
# Blocked:
safe_eval_math("__import__('os').system('rm -rf /')", {})
# → ValueError: Unsupported AST node type
API Surface¶
from agent_utilities.core.wasm_runner import WasmAgentRunner
runner = WasmAgentRunner(limit_memory_pages=16)
# Load pre-compiled WASM binary
with open("micro_agent.wasm", "rb") as f:
runner.load_agent(f.read())
# Execute with JSON I/O
result = runner.execute({
"action": "calculate_fees",
"base_fee": 150.0,
"state_fee": 50.0,
"expedited": True,
})
# → {"status": "success", "total_fee": 300.0, ...}
Integration Points¶
- Security Kernel (OS-5.1): Tool guard checks run before WASM dispatch
- Ontological Guardrails (OS-5.10): Arguments validated against OWL policies before sandbox entry
- Cognitive Scheduler (OS-5.2): WASM executions are tracked as agent processes with token quotas
- SandboxedExecutor (OS-5.6): Process-level isolation complement — WASM for untrusted code, subprocess for trusted scripts
Implementation Details¶
- Source Code:
wasm_runner.py(257 lines) - Classes:
WasmAgentRunner,safe_eval_math - Tests:
test_wasm_runner.py - Pillar: OS
- Package Export:
agent_utilities.core.WasmAgentRunner - Optional Dependency:
wasmtime(graceful fallback to emulation mode)