Skip to content

v1.0+: WASM-sandboxed plugin runtime as alternative to external-process model #37

@Metbcy

Description

@Metbcy

Context

v0.9.6 shipped the external-process plugin model: --plugin <manifest.toml> runs an external executable per matching component, JSON over stdin/stdout. Best-effort failures, no sandbox, plugins run as the CI user.

The "no sandbox" part is intentional for v0.9.x — the alternative (WASM) would have required wasmtime (~30MB binary, ~80 transitive deps) or wasmi (smaller but slower), both of which conflict with bomdrift's "single-binary, small dep tree" tenet. v0.9.7 explicitly deferred WASM to v1.0+.

For v1.0+: revisit. If the WASM ecosystem has a lighter runtime by then (or if external-process plugins prove insufficient for security-conscious deployments), add a WASM-backed plugin runtime as an alternative to the external-process model.

Scope (open-ended; this is a design-stage issue)

  • Survey the current state of small Rust WASM runtimes:

    • wasmtime (heavy, mature)
    • wasmi (lighter, no JIT, slower)
    • wasmer (similar tradeoffs to wasmtime)
    • wasmedge (heavier, edge-focused)
    • wamr (C, can't trivially use from Rust)

    Pick the one that passes "binary size impact <5 MB on bomdrift's release binary; transitive deps <30."

  • Define a WASM plugin ABI that matches the existing external-process protocol semantically:

    • Plugin exports a function (e.g. bomdrift_enrich(component_json_ptr, len) -> findings_json_ptr_len).
    • Host imports a small set of helpers (logging, maybe one now() if the plugin needs deterministic time).
    • Memory model: linear memory, no shared state between invocations.
  • Manifest extension: [plugin] type = "wasm" (or auto-detect by .wasm extension on exec).

  • Tests that validate sandbox semantics: a WASM plugin attempting a host-call outside the imported set must fail cleanly.

  • Docs in docs/src/plugins.md: when to use WASM (untrusted plugins) vs external-process (when you control the plugin source).

  • An example WASM plugin in examples/plugins/ written in Rust (compiled to wasm32-wasi or similar).

Constraints

  • Don't break the v0.9.6 external-process protocol. Both runtimes coexist; users pick per-plugin.
  • protocol_version: 1 on plugin manifests. Bump to 2 if the WASM ABI is meaningfully different.
  • Adopt the smallest viable WASM runtime. If the binary-size hit is unacceptable, consider feature-gating: cargo install bomdrift --features wasm-plugins.

Why this matters

The current external-process model is fine for trusted plugins (your org's banned-packages list). For untrusted plugin sources (community marketplace, third-party ecosystems), sandboxing is the difference between "this is a cool extension point" and "this is a remote-code-execution vector". A WASM runtime gives us the latter.

A note on commit signing

main requires verified signatures (the repo ships cosign-signed releases — we hold our own commits to the same bar).

You usually don't need to set up signing as a contributor — when a maintainer merges via "Merge" or "Squash", GitHub auto-signs the resulting commit and your unsigned PR-branch commits are fine. The friendlier path for everyone.

If you'd like your individual commits to land verbatim on main (so your name shows up in git blame), set up local signing once and your PR can be rebase-merged:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

Then add the same SSH public key under GitHub → Settings → SSH and GPG keys → Signing keys.

See CONTRIBUTING.md → Commit signing on main for the full picture. Either way, please don't sweat it — if your PR is otherwise great, the maintainer will pick a merge mode that works.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is neededquestionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions