WASM sandbox
Two reasons to run the scanner inside a WASM sandbox:
- You don’t trust the signatures. Third-party
.ndbsets, community YARA rules, anything you didn’t build yourself. - You don’t trust exav. Entirely reasonable — it is a young parser of hostile
input, and no amount of
#![forbid(unsafe_code)]is the same as a boundary the kernel or the runtime enforces. This is the option that needs no assumptions about exav’s own code being correct.
The same exav-core engine compiles to wasm32-wasip1 and runs under any WASI
runtime (wasmtime, wasmer, wazero) with no custom host code: you bring your
own audited runtime, and a bug anywhere inside exav costs you a crashed sandbox
rather than a compromised host.
Quick start
Section titled “Quick start”-
Build the WASM module:
Terminal window rustup target add wasm32-wasip1cargo build --release --target wasm32-wasip1 -p exav-core --features wasi-bin -
Strip debug names (~10% smaller):
Terminal window cargo install wasm-tools # if not installedwasm-tools strip -a target/wasm32-wasip1/release/exav-wasm.wasm -o exav.wasm -
Scan a file under wasmtime — signatures mounted at
/db, CWD at/:Terminal window wasmtime \--dir /path/to/sigs::/db \--dir .::. \exav.wasm \/db malware.exe
The --dir flags mount directories into the sandbox: --dir /path/to/sigs::/db
mounts the signature DB at /db, --dir .::. mounts the working directory at
/ (so relative paths work).
Output
Section titled “Output”Each scanned file produces one JSON ScanReport on stdout; progress and errors
go to stderr:
wasmtime --dir ./sigs::/db --dir .::. exav.wasm /db *.exe 2>/dev/null | jq '.verdict'{"verdict":"Clean","findings":[{"label":"type","detail":"PE32+ executable"}]}{"verdict":{"Infected":{"signature":"Win.Trojan.Agent-1234","offset":4096,"method":"Pattern"}},"findings":[]}{"verdict":{"LimitsExceeded":{"reason":"scan-size limit"}},"findings":[]}Verdict variants: Clean, Infected, LimitsExceeded, Unscannable,
PasswordProtected.
Why this matters
Section titled “Why this matters”When you load third-party signatures, the signature parser becomes part of your
attack surface — a malicious .ndb could trigger a parser overflow (RCE),
excessive allocation (DoS), or try to escape the process. Native code makes any
of these a host compromise. In a WASM sandbox:
Native exav |
WASM sandbox | |
|---|---|---|
| Signature trust | full host access | memory-limited WASM instance |
| Runtime | your code | wasmtime (Bytecode Alliance, audited) |
| Blast radius | entire host | isolated WASM module, fuel-limited |
| Custom host code | N/A | none — you bring your own wasmtime |
| Overhead | native speed | ~10–20% |
- Memory is bounded — the module can’t access memory outside its linear memory instance.
- No system calls — WASI restricts file/network access to explicitly mounted
paths; the module can’t read
/etc/passwdor reach the network. - Fuel limits — wasmtime can cap instruction counts, killing runaway modules.
- Panic isolation — a panic on corrupt input becomes a wasmtime trap; the host process is unaffected.
Limitations
Section titled “Limitations”- No daemon mode — each
wasmtime runloads the DB from scratch. For high-throughput scanning use a nativeexav --listen. - No stdin streaming — files must be on a mounted filesystem.
- No HTTP(S) scanning — the module can’t make network requests.
- DB loading is slower / bounded — the full ClamAV DB build wants ~6 GiB transient RAM, bounded here by the runtime’s memory limit. Use a prebuilt database for large signature sets.
Alternatives
Section titled “Alternatives”- Native
exav— best performance and full features, but trusts the scanner with host access. - Docker container — similar isolation, heavier (a full OS image vs one
.wasmfile).