The WASI Preview 2 stabilization in Bytecode Alliance's January 2024 announcement moved WebAssembly from "experimental sandbox" to "declarable capability surface." With wasmtime 25.0, WasmEdge 0.14, and wasmCloud 1.1 shipping Preview 2 support in 2024 and 2025, teams now have concrete choices for running untrusted user code, plugin ecosystems, and policy logic with per-module host-function control. The 2025 security model is a composition of four things: Wasm 2.0 core isolation (sandboxed linear memory, structured control flow, no raw pointers), the WIT interface description language, the component model's typed boundaries, and WASI's capability-based host APIs. Understanding where each layer's guarantees start and end is how you avoid building a plugin system that looks secure in a demo and leaks in production. This post walks the 2025 state of WASI, what Preview 2 changed, and what to watch when embedding wasmtime in build tooling, SBOM scanners, and policy engines.
What changed in WASI Preview 2 versus Preview 1?
Preview 2 replaced snapshot_preview1's flat POSIX-like surface with a typed, componentized interface model. Preview 1 exposed functions like fd_read and path_open directly against a file descriptor table; Preview 2 defines interfaces such as wasi:filesystem/preopens@0.2.0, wasi:io/streams@0.2.0, and wasi:http/outgoing-handler@0.2.0 written in WIT (WebAssembly Interface Types) and compiled through the component model. Hosts no longer must implement a fixed syscall table; instead they supply implementations of the interfaces the component imports. wasmtime 25 supports both a p1 adapter (for legacy modules compiled against Preview 1) and native p2 components. The practical upshot: a build-pipeline plugin can declare it needs wasi:http/outgoing-handler but not wasi:filesystem, and the host can refuse to link the former without reading a config file.
How does capability-based isolation actually work?
Every resource handle is an opaque, unforgeable reference minted by the host. When a wasmtime host preopens /workspace as a directory capability, the guest receives an integer handle scoped to that preopen; there is no syscall to open /etc/passwd by absolute path because no capability to the root filesystem was passed in. The same pattern applies to sockets: wasi:sockets/tcp@0.2.0 requires the host to hand the guest a tcp-socket resource, and the guest cannot forge one. This is classical object-capability (OCap) discipline, the same model as Capsicum and seL4's cap_t. The threat model this defeats is ambient authority: a compromised component cannot reach a resource the host never handed it. The threat model it does not defeat is misuse of legitimately granted capabilities, which is why per-interface rate limits and resource budgets matter.
Where does the sandbox actually enforce isolation?
At three layers: Wasm's control-flow integrity, wasmtime's memory model, and operating-system-level defenses. Wasm 2.0 core guarantees structured control flow (no computed jumps outside function boundaries), linear memory bounds-checked on every load/store, and strict separation of code and data. wasmtime uses Cranelift as its compiler and applies explicit bounds checks or virtual-memory guard pages; the MemoryConfig::guard_pages setting on wasmtime 25 defaults to 2 GiB of reserved guard space on 64-bit hosts so most bounds checks fold into hardware-trapped page faults. On top of that, wasmtime enables Spectre mitigations via Cranelift's enable_heap_access_spectre_mitigation flag, and on Linux hosts it integrates with seccomp-bpf when embedding in processes like spin or wasmCloud's host runtime. The cumulative effect is that a guest-level exploit must escape linear memory, bypass CFI, and reach native code, which is substantially harder than a native process escape.
What CVEs and weaknesses have emerged against WASI runtimes?
Small but instructive. CVE-2023-26489 (wasmtime 6.0.1) was a bounds-check oversight in the Cranelift-generated code when using memory.grow with certain linear memory configurations, letting a crafted module read a small amount of host memory; it was disclosed and patched in March 2023. CVE-2024-32006 affected WasmEdge 0.13.5 with a host-function binding error. Beyond core runtimes, the typical real-world issue is interface design, not sandbox escape: a host that implements wasi:filesystem/preopens by handing out the project root as a single capability collapses the capability model back to ambient authority. wasmtime's security advisories are tracked at GitHub bytecodealliance/wasmtime/security/advisories, and the Bytecode Alliance's security.md policy sets a 90-day embargo window. Teams deploying Wasm in supply-chain tooling should subscribe to these advisories and pin runtime versions in CI.
How should supply-chain tools embed Wasm safely?
Treat every component as a tenant. Use wasmtime::Engine with Config::consume_fuel(true) to bound execution, Config::epoch_interruption(true) to allow wall-clock preemption, and StoreLimits to cap memory, table, and instance counts. Define WIT worlds that expose only the narrow surface the plugin needs: for an SBOM parser plugin, the world should import wasi:io/streams@0.2.0 and a custom sbom:scan/reader@1.0.0 interface, and nothing else. Use wasmtime-wasi 25's WasiCtxBuilder::preopened_dir only for directories the plugin needs, with DirPerms::READ and FilePerms::READ where writes are not required. For network plugins, implement wasi:http/outgoing-handler behind a policy that enforces allowlisted hosts and concurrency limits. wasmCloud's 1.1 capability providers demonstrate this model at scale.
How Safeguard Helps
Safeguard policy engines can run third-party rule logic as signed Wasm components instead of arbitrary Lua or JavaScript, giving you an auditable trust boundary around custom policy. Components are signed with Sigstore cosign 2.4 and verified against a transparent Rekor log before loading into the wasmtime host, and each component's WIT imports are evaluated against an allowlist per tenant. Runtime telemetry records fuel consumption and capability invocation counts, so deviations show up as anomalies. When a new WASI runtime CVE lands, Safeguard's policy-gate module flags affected tenants and surfaces the migration path in findings.