DEV Community

SorrelThorncroft
SorrelThorncroft

Posted on

WASI Capabilities Depend on the Runtime, Not the Label

WASI Capabilities Depend on the Runtime, Not the Label

WebAssembly modules are often described as sandboxed because they cannot directly open a file or socket. WASI gives them a controlled interface to the outside world. The security value comes from the capabilities the host passes in, not from the claim that a module is WebAssembly.

A review that stops at "the code runs in Wasm" misses the host shim. The runtime may preopen directories, expose environment data, provide clocks, or attach network handles. A component that imports wasi:filesystem needs a host implementation that grants the operations. The module's source code does not prove what the deployment granted.

Two kinds of capability

WASI documentation distinguishes link-time capabilities from runtime capabilities. A link-time capability is part of the interface a component imports, such as a clock function. A runtime capability is a handle supplied when the component starts, such as a preopened directory or a network object.

That distinction helps explain why a module can be portable and still dangerous. The same bytecode can receive a different set of preopened directories in two environments. One deployment may expose a read-only documentation tree. Another may expose a shared credential directory because an operator wanted a quick fix.

WASI 0.1 modules work primarily with resources the runtime hands them at startup, especially preopened file descriptors. WASI 0.2 made several capabilities explicit in the component interface. WASI 0.3, released in 2026, changes the shape of async and I/O interfaces and makes some capability wiring implicit at the world level. The security question remains: which host resources does the chosen world and runtime make reachable?

Review the host shim

Start with the world the component targets. List every imported interface, then inspect the host implementation that supplies it. A test that only instantiates the guest is not enough. The production runtime may add filesystem access, environment variables, HTTP handlers, or sockets that were not present in the test harness.

For filesystem access, verify the preopened directories, the mount view, and whether writes are permitted. For network access, verify whether the runtime grants a network handle and whether that handle allows outbound connections, listening, or only a narrow operation. For environment data, treat variables as configuration and potential secrets. Do not assume that a Wasm boundary automatically redacts them.

The host shim is code too. It should have tests for denied operations, not only successful calls. A component that asks for a path outside its preopened directory should receive a clean denial. A socket operation that lacks a network capability should not trigger a fallback through a Node.js or system API with broader authority.

Composition and ambient authority

WASI's design aims to replace ambient authority with explicit resources, but the host can reintroduce ambient authority. A JavaScript shim that runs on Node.js may inherit the Node process's file and network access. A custom runtime may expose a broad host function because it was convenient for one application.

Review composition boundaries carefully. A component model can link components together, and a capability may be adapted or attenuated at a boundary. Ask who performs the adaptation, whether it narrows or widens the resource, and whether the runtime records the decision.

This is especially important for plugins. A plugin that runs beside trusted code may look isolated in the source tree but share a runtime with a powerful host shim. If plugins can import a generic HTTP capability, they can probably reach whatever the shim permits unless there is an additional policy. If they receive only a signed, scoped request object, the boundary is easier to reason about.

Operational tests

A practical validation set includes:

  • a read inside the approved directory;
  • a read outside it;
  • a write to a read-only mount;
  • a network call with and without the network capability;
  • a clock or random operation that is intentionally unavailable;
  • a restart with the production host configuration.

Log the capability request, the denial, and the runtime identity. Do not log secret file contents or full environment values. Record the exact runtime version because WASI interfaces and shim behavior change between releases.

The design rule

Treat a WASI deployment as a resource contract. The contract names the module, its world, its preopened directories, its network handles, its environment, and the runtime that implements them. The module is only one side of the boundary.

That contract should be narrow enough that a compromised component can do little beyond its intended job. If it needs one read-only configuration directory, do not hand it the application root. If it needs to send a request to one service, do not grant a generic outbound socket. WASI makes that discipline possible. It does not choose the policy for you.

References

Top comments (0)