Security Best Practices
Practical guidance for running Wasm modules securely with Endive.
Writing Safe Host Functions
Host functions are the primary attack surface — they cross the sandbox boundary and execute with full JVM privileges.
Always validate arguments from Wasm:
HostFunction readMemory = (Instance instance, long... args) -> {
int offset = (int) args[0];
int length = (int) args[1];
// Validate bounds BEFORE accessing memory
var memory = instance.memory();
if (offset < 0 || length < 0 || offset + length > memory.pages() * 65536) {
throw new TrapException("out of bounds memory access");
}
byte[] data = memory.readBytes(offset, length);
// ... process data safely
return new long[0];
};
Key rules:
- Validate all memory offsets and lengths before reading/writing
- Never use Wasm-provided values as array indices without bounds checking
- Be cautious with string decoding — enforce maximum lengths
- Avoid exposing file paths, SQL queries, or shell commands derived from Wasm input
WASI Sandboxing
WASI file access does not enforce path sandboxing by default. Passing the host filesystem directly exposes all files the JVM process can access.
Use a virtual filesystem:
var options = WasiOptions.builder()
// Use ZeroFS to restrict access to specific directories
.withDirectory("/guest/data", Path.of("/host/sandboxed/data"))
.withStdout(myStdout)
.withStderr(myStderr)
.build();
Never use inheritSystem() with untrusted modules — it grants full host filesystem, environment, and stdio access.
Resource Limits
Wasm modules can consume unbounded CPU and memory. Always set limits when running untrusted code.
CPU timeout via thread interruption:
var executor = Executors.newSingleThreadExecutor();
var future = executor.submit(() -> instance.export("run").apply());
try {
var result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true); // interrupts the Wasm execution
}
See CPU Limits for more patterns.
Compiler Security
The runtime and build-time compilers translate Wasm to JVM bytecode for performance. When running untrusted modules:
- Prefer the interpreter for maximum sandbox assurance — it doesn't generate JVM bytecode
- Protect compiler cache directories with restrictive permissions (
chmod 700) if using the directory cache - Set JVM heap limits when compiling large untrusted modules to prevent resource exhaustion
Dependency Supply Chain
- All Endive dependencies are checked against the Bytecode Alliance allowed license list in CI
- Dependency vulnerabilities are scanned nightly via OWASP Dependency-Check
- Dependabot is enabled for automated security updates
- All dependency updates are manually reviewed before merging