Security
Hardening Guide
Last updated 2026-04-13
Hardening Guide
This document tracks the hostile-input and fuzzing entry points that back Phase 10 of the roadmap.
Verified Suites
Run the maintained hardening checks with:
scripts/run-hardening.sh
That runs:
cargo test -p mustard --test security_hostile_inputscargo test -p mustard --test property_generated_executioncargo test -p mustard --test property_roundtripcargo test -p mustard --test property_snapshot_roundtripcargo test -p mustard-sidecar --test hostile_protocolnpm run buildnode scripts/seed-fuzz-corpus.ts- short executed
cargo-fuzzsmoke forparser,snapshot_load, andsidecar_protocol
Fuzz Targets
The fuzz/ package contains libFuzzer entry points for the main untrusted
input boundaries:
parserir_loweringbytecode_validationbytecode_executionsnapshot_loadsidecar_protocol
Example usage after installing cargo-fuzz:
cargo fuzz run parser --manifest-path fuzz/Cargo.toml
cargo fuzz run sidecar_protocol --manifest-path fuzz/Cargo.toml
The maintained corpus seeds are generated into fuzz/corpus/ by
scripts/seed-fuzz-corpus.ts. That script starts from:
- curated supported guest programs
- curated unsupported/fail-closed programs
- real compiled-program bytes from the public Node wrapper
- real suspended snapshot bytes from resumable public API flows
- valid and hostile sidecar protocol request lines
The repository ignores the generated corpus directory so local and CI fuzzing can grow it over time without polluting commits.
Mutation-style guard checks now live in tests/hardening/mutation-guards.test.js
and run through:
npm run test:mutation-guards
That suite mutates a small set of validator inputs, serialized progress blobs, limit thresholds, and structured-boundary values to ensure the fail-closed guards stay wired up without putting the checks in the default fast Node test lane.
CI And Scheduled Jobs
.github/workflows/ci.ymlnow runsscripts/run-hardening.shin the PR lane on Ubuntu, which executes the hostile-input suites plus short fuzz smoke forparser,snapshot_load, andsidecar_protocol..github/workflows/fuzz.ymlruns nightly and on manual dispatch. It restores the cachedfuzz/corpus/directory, refreshes baseline seeds, runs longer sanitizer-backed fuzzing across all current targets, runs the mutation-style guard suite outside the default PR path, and uploads both the grown corpus and any crash artifacts fromfuzz/artifacts/.
For local tuning, scripts/run-hardening.sh accepts:
MUSTARD_FUZZ_TARGETSto override the fuzz target listMUSTARD_FUZZ_SECONDSto change per-target runtimeMUSTARD_FUZZ_TOOLCHAINto pick a non-default toolchainMUSTARD_FUZZ_ARTIFACT_ROOTto redirect crash artifacts
Denial-of-Service Audit
Current hostile-input pressure points and mitigations:
- Parser and IR lowering accept untrusted source text. Property tests cover arbitrary byte slices, and sidecar mode remains the recommended deployment boundary for adversarial inputs.
- Bytecode and snapshot loading treat serialized blobs as untrusted. The
verified suites mutate valid blobs, fuzz loaders with arbitrary bytes, and
assert that failures stay host-safe. The dedicated
crates/mustard/tests/snapshot_policy_security.rssuite also verifies that loaded snapshots cannot resume until the host reasserts allowed capability names and explicit runtime limits. - Bytecode execution is bounded by instruction, heap, allocation, and call-depth limits. The hardening suite injects over-budget workloads and checks for guest-safe failures.
- Sidecar protocol decoding treats every input line as hostile. The sidecar library path is fuzzable directly and has integration coverage for malformed, truncated, and semantically hostile requests.
- Cooperative cancellation is now part of the core runtime boundary. The new cancellation suite asserts guest-safe failures both for in-flight compute and for suspended async host waits. Hard-stop behavior still remains OS-process termination in sidecar mode when hosts need preemptive kill guarantees.
Residual risk notes:
- The parser/lowering path is still recursive. The hostile-source test runs in a larger-stack thread to keep the regression suite reproducible while avoiding false negatives from the default test-thread stack.
- In-process addon mode is still best-effort containment only. Use sidecar mode plus host-managed OS sandboxing for adversarial deployments.
Kill and Cancellation Coverage
The current kill/cancellation evidence is intentionally split by deployment mode:
- Core cooperative cancellation is covered by
crates/mustard/tests/cancellation.rs, which interrupts running guest code and cancels suspended async host waits without letting guesttry/catchconvert the host abort into ordinary guest control flow. - Addon-mode cancellation plumbing is covered by
tests/node/cancellation.test.js, which verifies already-aborted signals, explicitProgress.cancel(), and cancellation while guest async code is awaiting a host promise. - Addon-mode delayed-await behavior is still covered by
tests/node/coverage-audit.test.js, which verifies that merely dropping or delaying the caller's immediate await does not itself inject cancellation. - Sidecar hard-stop behavior is covered by
crates/mustard-sidecar/tests/protocol.rs, which forcefully terminates a live sidecar process and verifies that a fresh sidecar can be started cleanly afterward. - Same-thread addon compute is still cooperative rather than preemptive. If the host needs a hard kill while native execution is on the Node main thread, sidecar termination remains the stronger control.