feat(wasm-runtime): add sandbox exec, logging, and timestamp host functions#33
feat(wasm-runtime): add sandbox exec, logging, and timestamp host functions#33
Conversation
…ing host functions Implement host-side sandbox capabilities for WASM challenges via a new `platform_sandbox` import module with three host functions: - `sandbox_exec`: Deserializes SandboxExecRequest from WASM memory, executes commands via std::process::Command with policy enforcement (allowed/blocked commands, timeout via wait-timeout, output size limits, environment whitelisting, working directory sandboxing), and writes serialized SandboxExecResponse back to WASM memory. - `get_timestamp`: Returns Unix timestamp in milliseconds, supporting both real-time and deterministic modes for reproducible evaluation. - `log_message`: Reads log messages from WASM memory and forwards them to the host tracing infrastructure with proper level mapping (Trace through Error). New SandboxPolicy struct mirrors the existing NetworkPolicy pattern with fields for allowed/blocked commands, max execution time, max output bytes, network access control, working directory, and environment whitelisting. SandboxHostFunctions implements the HostFunctionRegistrar trait, consistent with the existing NetworkHostFunctions pattern. The runtime now supports multiple registrars via instantiate_with_registrars(), and InstanceConfig is extended with sandbox_policy and timestamp_mode fields (using ..Default::default() in existing call sites for backward compatibility). Files changed: - crates/wasm-runtime-interface/src/sandbox.rs (new): Full sandbox module with SandboxPolicy, SandboxState, SandboxHostFunctions, request/response types, memory read/write helpers, and comprehensive unit tests - crates/wasm-runtime-interface/src/runtime.rs: Added SandboxState to RuntimeState, sandbox_policy/timestamp_mode to InstanceConfig, and instantiate_with_registrars() supporting multiple registrars - crates/wasm-runtime-interface/src/lib.rs: Export sandbox module and types - crates/wasm-runtime-interface/Cargo.toml: Added wait-timeout dependency - bins/validator-node/src/wasm_executor.rs: Use ..Default::default() for new InstanceConfig fields - Cargo.lock: Updated with wait-timeout 0.2.1
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughIntroduces in-process sandbox execution support to the WASM runtime interface. Adds a new Changes
Sequence DiagramsequenceDiagram
participant WASM as WASM Module
participant Host as Host Runtime
participant Sandbox as SandboxState
participant Exec as Command Executor
participant Sys as System
WASM->>Host: call sandbox_exec(request_ptr)
Host->>Host: deserialize SandboxExecRequest from memory
Host->>Sandbox: is_command_allowed(command)
Sandbox->>Sandbox: check policy allow/block lists
Sandbox-->>Host: Result<(), SandboxError>
alt Command Blocked
Host-->>WASM: error response
else Command Allowed
Host->>Exec: spawn Command with env, stdin, working_dir
Exec->>Sys: execute subprocess
Sys-->>Exec: exit code, stdout, stderr
Exec->>Exec: enforce output_size limit
Exec->>Sandbox: increment executions_count
Exec-->>Host: SandboxExecResponse
Host->>Host: serialize response to memory
Host-->>WASM: success with response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…t-functions # Conflicts: # crates/wasm-runtime-interface/Cargo.toml # crates/wasm-runtime-interface/src/runtime.rs
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/wasm-runtime-interface/src/lib.rs (1)
1-5:⚠️ Potential issue | 🟡 MinorModule doc comment is stale — now covers more than network access.
The crate-level doc says "host interface definitions for network access" but this PR adds sandbox functionality (exec, timestamps, logging). Consider updating the doc to reflect the broader scope.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/lib.rs` around lines 1 - 5, Update the crate-level doc comment in lib.rs to reflect the broader host interface (not just network access): change the opening lines that currently state "WASM runtime host interface definitions for network access" to a concise summary that includes sandboxing features such as process execution, timestamps, and logging in addition to network access; reference the crate as providing the declarative host function surface exposed to challenge WASM modules (the same intent as before) and mention the new sandboxing/runtime controls (exec, timestamps, logging) so readers of the crate documentation understand its full scope.
🧹 Nitpick comments (4)
crates/wasm-runtime-interface/src/sandbox.rs (3)
225-229: Silently ignoring stdin write errors may hide failures.Line 228 discards the result of
write_all. If the write fails (e.g., broken pipe, buffer exhaustion), the command runs with partial or no input, which could produce confusing results. Consider at least logging a warning.Log stdin write failures
if let Some(stdin_data) = &request.stdin { if let Some(mut stdin) = child.stdin.take() { use std::io::Write; - let _ = stdin.write_all(stdin_data); + if let Err(e) = stdin.write_all(stdin_data) { + warn!(error = %e, "failed to write stdin data to child process"); + } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 225 - 229, The stdin write result is currently discarded (request.stdin + child.stdin.take() + write_all), which can hide failures; update the block that writes to child.stdin so it checks the Result from write_all and at minimum logs a warning with the error details (e.g., using log::warn! or tracing::warn!) including contextual info (request id/command or similar) — alternatively propagate the error from the enclosing function (return Err) if that better fits error handling in sandbox.rs; ensure you reference the same request.stdin and child.stdin.take() code path when making this change.
247-262: Output is fully buffered in memory before truncation — risk of OOM for large outputs.
read_to_endon lines 249 and 254 reads the entire output into memory, then truncates afterward (lines 257–262). A command producing gigabytes of output would consume that much host memory before truncation kicks in.Consider using
Read::take(max_output_bytes as u64)to cap the read at the policy limit:Bounded read
let mut stdout = Vec::new(); if let Some(mut out) = child.stdout.take() { - let _ = out.read_to_end(&mut stdout); + let _ = out.take(self.policy.max_output_bytes as u64).read_to_end(&mut stdout); } let mut stderr = Vec::new(); if let Some(mut err) = child.stderr.take() { - let _ = err.read_to_end(&mut stderr); + let _ = err.take(self.policy.max_output_bytes as u64).read_to_end(&mut stderr); } - - if stdout.len() > self.policy.max_output_bytes { - stdout.truncate(self.policy.max_output_bytes); - } - if stderr.len() > self.policy.max_output_bytes { - stderr.truncate(self.policy.max_output_bytes); - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 247 - 262, The code currently calls read_to_end on child.stdout and child.stderr (variables out/err) which can OOM before you later truncate; change the reads to bounded reads using Read::take(self.policy.max_output_bytes as u64) (or out.take(...) / err.take(...)) and read from those into stdout/stderr (or use io::copy from the take() wrapper) so the OS never buffers more than self.policy.max_output_bytes; remove the post-read truncation since the take() already enforces the limit.
556-749: Tests provide good baseline coverage.Policy enforcement, log level mapping, timestamp modes, basic exec, output truncation, and counter reset are all covered. Note that
test_sandbox_exec_simple_commandandtest_sandbox_exec_output_truncationdepend on theechobinary being available, making them platform-dependent (will fail on some CI environments or Windows).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 556 - 749, Tests test_sandbox_exec_simple_command and test_sandbox_exec_output_truncation rely on the external "echo" binary and are platform-dependent; annotate or gate these tests so they don't run on platforms without that binary (e.g., Windows). Modify the two tests (test_sandbox_exec_simple_command and test_sandbox_exec_output_truncation) to use a conditional compilation attribute such as #[cfg(unix)] or #[cfg(not(target_os = "windows"))] so they only run on Unix-like CI, or alternatively replace them with a platform-independent approach (e.g., invoking a built-in test helper or injecting stdout via SandboxState::handle_exec mocks) to avoid calling external binaries directly.crates/wasm-runtime-interface/src/runtime.rs (1)
133-165:RuntimeStateconstructor is getting large — consider a builder or config struct.With 9 parameters and the
#[allow(clippy::too_many_arguments)]suppression,RuntimeState::newis approaching the point where a builder pattern or passingInstanceConfigdirectly (plus computed state objects) would improve ergonomics. Not blocking, but something to keep in mind as more features are added.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/wasm-runtime-interface/src/runtime.rs` around lines 133 - 165, RuntimeState::new currently takes nine arguments (network_policy, network_state, sandbox_state, memory_export, challenge_id, validator_id, restart_id, config_version, limits) and is flagged with #[allow(clippy::too_many_arguments)]; refactor by introducing a config struct (e.g., InstanceConfig or RuntimeConfig) that groups related parameters (network_policy, memory_export, challenge_id, validator_id, restart_id, config_version, limits) and have RuntimeState::new accept that config plus the already-composed state objects (NetworkState, SandboxState), or implement a RuntimeStateBuilder that exposes clear setters for each field and a build() that constructs RuntimeState; update all call sites to construct the config/builder instead of passing nine positional args and remove the clippy allow attribute once refactored.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/wasm-runtime-interface/src/sandbox.rs`:
- Around line 61-72: Default SandboxPolicy currently treats an empty
allowed_commands as "allow all", so change the default to a secure-by-default
posture by adding a new bool field (e.g., sandbox_enabled) to SandboxPolicy and
set it to false in the Default impl, or alternatively populate allowed_commands
with an explicit safe whitelist; then update is_command_allowed to first check
sandbox_enabled (deny all when false) or to treat an empty allowed_commands as
deny-all instead of allow-all; ensure the permissive() constructor still
explicitly sets sandbox_enabled = true (or populates allowed_commands to allow
all) so tests/dev keep the wide-open behavior.
- Around line 50-58: handle_exec currently ignores SandboxPolicy::allow_network
so a policy with allow_network=false doesn't prevent child processes from making
network calls; update handle_exec to respect this flag by early-returning an
error (or calling todo!/unimplemented! with a clear message) when allow_network
is false, or wire in the intended OS-level enforcement path (e.g.,
seccomp/namespace setup) before spawning the child; reference
SandboxPolicy::allow_network, the handle_exec function, and the permissive()
constructor so reviewers can locate the policy and decide whether to implement
enforcement or explicitly block/guard execution.
- Around line 232-262: The code currently calls child.wait_timeout(timeout)
before draining the child's stdout/stderr, which can deadlock if the child
blocks writing to full OS pipes; fix by reading stdout and stderr concurrently
while waiting: take child.stdout and child.stderr (as in the existing references
to child.stdout.take() / child.stderr.take()), spawn reader threads (or async
tasks) that stream each pipe into buffers and enforce the same
self.policy.max_output_bytes limit/truncation, then call
child.wait_timeout(timeout) and join the reader threads to collect outputs; on
errors, propagate SandboxError::ExecutionFailed with the reader error text and
preserve returning SandboxError::Timeout(self.policy.max_execution_time_ms) if
the wait times out, ensuring child.kill() is still called when needed.
---
Outside diff comments:
In `@crates/wasm-runtime-interface/src/lib.rs`:
- Around line 1-5: Update the crate-level doc comment in lib.rs to reflect the
broader host interface (not just network access): change the opening lines that
currently state "WASM runtime host interface definitions for network access" to
a concise summary that includes sandboxing features such as process execution,
timestamps, and logging in addition to network access; reference the crate as
providing the declarative host function surface exposed to challenge WASM
modules (the same intent as before) and mention the new sandboxing/runtime
controls (exec, timestamps, logging) so readers of the crate documentation
understand its full scope.
---
Nitpick comments:
In `@crates/wasm-runtime-interface/src/runtime.rs`:
- Around line 133-165: RuntimeState::new currently takes nine arguments
(network_policy, network_state, sandbox_state, memory_export, challenge_id,
validator_id, restart_id, config_version, limits) and is flagged with
#[allow(clippy::too_many_arguments)]; refactor by introducing a config struct
(e.g., InstanceConfig or RuntimeConfig) that groups related parameters
(network_policy, memory_export, challenge_id, validator_id, restart_id,
config_version, limits) and have RuntimeState::new accept that config plus the
already-composed state objects (NetworkState, SandboxState), or implement a
RuntimeStateBuilder that exposes clear setters for each field and a build() that
constructs RuntimeState; update all call sites to construct the config/builder
instead of passing nine positional args and remove the clippy allow attribute
once refactored.
In `@crates/wasm-runtime-interface/src/sandbox.rs`:
- Around line 225-229: The stdin write result is currently discarded
(request.stdin + child.stdin.take() + write_all), which can hide failures;
update the block that writes to child.stdin so it checks the Result from
write_all and at minimum logs a warning with the error details (e.g., using
log::warn! or tracing::warn!) including contextual info (request id/command or
similar) — alternatively propagate the error from the enclosing function (return
Err) if that better fits error handling in sandbox.rs; ensure you reference the
same request.stdin and child.stdin.take() code path when making this change.
- Around line 247-262: The code currently calls read_to_end on child.stdout and
child.stderr (variables out/err) which can OOM before you later truncate; change
the reads to bounded reads using Read::take(self.policy.max_output_bytes as u64)
(or out.take(...) / err.take(...)) and read from those into stdout/stderr (or
use io::copy from the take() wrapper) so the OS never buffers more than
self.policy.max_output_bytes; remove the post-read truncation since the take()
already enforces the limit.
- Around line 556-749: Tests test_sandbox_exec_simple_command and
test_sandbox_exec_output_truncation rely on the external "echo" binary and are
platform-dependent; annotate or gate these tests so they don't run on platforms
without that binary (e.g., Windows). Modify the two tests
(test_sandbox_exec_simple_command and test_sandbox_exec_output_truncation) to
use a conditional compilation attribute such as #[cfg(unix)] or
#[cfg(not(target_os = "windows"))] so they only run on Unix-like CI, or
alternatively replace them with a platform-independent approach (e.g., invoking
a built-in test helper or injecting stdout via SandboxState::handle_exec mocks)
to avoid calling external binaries directly.
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct SandboxPolicy { | ||
| pub allowed_commands: Vec<String>, | ||
| pub blocked_commands: Vec<String>, | ||
| pub max_execution_time_ms: u64, | ||
| pub max_output_bytes: usize, | ||
| pub allow_network: bool, | ||
| pub working_directory: PathBuf, | ||
| pub environment_whitelist: Vec<String>, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify that allow_network is never read/checked outside of tests and struct construction
rg -n 'allow_network' --type rust -C2Repository: PlatformNetwork/platform-v2
Length of output: 1894
🏁 Script executed:
#!/bin/bash
# Find the handle_exec function to see if it checks allow_network
rg -n 'handle_exec|fn.*exec' --type rust -A5 | head -100Repository: PlatformNetwork/platform-v2
Length of output: 7428
🏁 Script executed:
#!/bin/bash
# Check the full SandboxPolicy implementation including constructors and methods
rg -n 'impl SandboxPolicy|fn.*permissive|fn.*new' --type rust -A10Repository: PlatformNetwork/platform-v2
Length of output: 50384
🏁 Script executed:
#!/bin/bash
# Get the full handle_exec function implementation
sed -n '185,250p' crates/wasm-runtime-interface/src/sandbox.rsRepository: PlatformNetwork/platform-v2
Length of output: 2244
🏁 Script executed:
#!/bin/bash
# Search for any conditional check on allow_network (any pattern like if/match with allow_network)
rg 'allow_network' --type rust -B2 -A2 crates/wasm-runtime-interface/src/sandbox.rsRepository: PlatformNetwork/platform-v2
Length of output: 929
allow_network field is declared but never enforced in handle_exec.
SandboxPolicy::allow_network (line 56) is set to false by default and in permissive(), but handle_exec never checks this flag before spawning the child process. A spawned command can freely make network calls regardless of this setting.
If network restriction is intended, it requires OS-level enforcement (e.g., seccomp, namespaces) or at minimum a pre-exec check. If this is placeholder for future work, add a todo!() / unimplemented!() guard or clearly document the intended behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 50 - 58,
handle_exec currently ignores SandboxPolicy::allow_network so a policy with
allow_network=false doesn't prevent child processes from making network calls;
update handle_exec to respect this flag by early-returning an error (or calling
todo!/unimplemented! with a clear message) when allow_network is false, or wire
in the intended OS-level enforcement path (e.g., seccomp/namespace setup) before
spawning the child; reference SandboxPolicy::allow_network, the handle_exec
function, and the permissive() constructor so reviewers can locate the policy
and decide whether to implement enforcement or explicitly block/guard execution.
| impl Default for SandboxPolicy { | ||
| fn default() -> Self { | ||
| Self { | ||
| allowed_commands: Vec::new(), | ||
| blocked_commands: Vec::new(), | ||
| max_execution_time_ms: 30_000, | ||
| max_output_bytes: 1024 * 1024, | ||
| allow_network: false, | ||
| working_directory: PathBuf::from("/tmp"), | ||
| environment_whitelist: Vec::new(), | ||
| } | ||
| } |
There was a problem hiding this comment.
Default SandboxPolicy allows execution of any command — consider a secure-by-default posture.
When allowed_commands is empty (the default), is_command_allowed permits every command (line 106 only checks the allowlist when it's non-empty). Combined with an empty blocked_commands, the default policy lets a WASM module run arbitrary OS processes on the host.
This is a security-sensitive default for a sandbox boundary. Consider either:
- Requiring an explicit opt-in (e.g., a
sandbox_enabled: boolgate that defaults tofalse, blocking all exec unless enabled), or - Defaulting
allowed_commandsto a safe whitelist rather than treating empty as "allow all".
The permissive() constructor can remain wide-open for development/test use.
Also applies to: 94-117
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 61 - 72, Default
SandboxPolicy currently treats an empty allowed_commands as "allow all", so
change the default to a secure-by-default posture by adding a new bool field
(e.g., sandbox_enabled) to SandboxPolicy and set it to false in the Default
impl, or alternatively populate allowed_commands with an explicit safe
whitelist; then update is_command_allowed to first check sandbox_enabled (deny
all when false) or to treat an empty allowed_commands as deny-all instead of
allow-all; ensure the permissive() constructor still explicitly sets
sandbox_enabled = true (or populates allowed_commands to allow all) so tests/dev
keep the wide-open behavior.
| let exit_status = match child.wait_timeout(timeout) { | ||
| Ok(Some(status)) => status, | ||
| Ok(None) => { | ||
| let _ = child.kill(); | ||
| let _ = child.wait(); | ||
| return Err(SandboxError::Timeout(self.policy.max_execution_time_ms)); | ||
| } | ||
| Err(e) => { | ||
| let _ = child.kill(); | ||
| return Err(SandboxError::ExecutionFailed(e.to_string())); | ||
| } | ||
| }; | ||
|
|
||
| let duration = start.elapsed(); | ||
|
|
||
| let mut stdout = Vec::new(); | ||
| if let Some(mut out) = child.stdout.take() { | ||
| let _ = out.read_to_end(&mut stdout); | ||
| } | ||
|
|
||
| let mut stderr = Vec::new(); | ||
| if let Some(mut err) = child.stderr.take() { | ||
| let _ = err.read_to_end(&mut stderr); | ||
| } | ||
|
|
||
| if stdout.len() > self.policy.max_output_bytes { | ||
| stdout.truncate(self.policy.max_output_bytes); | ||
| } | ||
| if stderr.len() > self.policy.max_output_bytes { | ||
| stderr.truncate(self.policy.max_output_bytes); | ||
| } |
There was a problem hiding this comment.
Pipe deadlock: reading stdout/stderr after wait_timeout can hang or spuriously time out.
The code calls child.wait_timeout(timeout) (line 232) before draining stdout/stderr (lines 247–255). If the child writes more than the OS pipe buffer (~64 KB on Linux), it will block on the write syscall and never exit — causing wait_timeout to hit the deadline even though the command isn't slow, it's just blocked on I/O.
The standard fix is to drain stdout/stderr concurrently with (or before) waiting. For example, spawn threads to read the pipes, or use std::process::Command output-capture via .output() combined with a timeout wrapper, or read in a loop alongside the wait.
Sketch of a fix using a dedicated reader thread
let mut child = cmd
.spawn()
.map_err(|e| SandboxError::ExecutionFailed(e.to_string()))?;
if let Some(stdin_data) = &request.stdin {
if let Some(mut stdin) = child.stdin.take() {
use std::io::Write;
let _ = stdin.write_all(stdin_data);
}
}
- let exit_status = match child.wait_timeout(timeout) {
+ // Drain stdout/stderr on background threads to prevent pipe-buffer deadlocks.
+ let stdout_handle = child.stdout.take().map(|mut out| {
+ std::thread::spawn(move || {
+ let mut buf = Vec::new();
+ let _ = out.read_to_end(&mut buf);
+ buf
+ })
+ });
+ let stderr_handle = child.stderr.take().map(|mut err| {
+ std::thread::spawn(move || {
+ let mut buf = Vec::new();
+ let _ = err.read_to_end(&mut buf);
+ buf
+ })
+ });
+
+ let exit_status = match child.wait_timeout(timeout) {
Ok(Some(status)) => status,
Ok(None) => {
let _ = child.kill();
let _ = child.wait();
return Err(SandboxError::Timeout(self.policy.max_execution_time_ms));
}
Err(e) => {
let _ = child.kill();
return Err(SandboxError::ExecutionFailed(e.to_string()));
}
};
let duration = start.elapsed();
- let mut stdout = Vec::new();
- if let Some(mut out) = child.stdout.take() {
- let _ = out.read_to_end(&mut stdout);
- }
-
- let mut stderr = Vec::new();
- if let Some(mut err) = child.stderr.take() {
- let _ = err.read_to_end(&mut stderr);
- }
+ let mut stdout = stdout_handle
+ .and_then(|h| h.join().ok())
+ .unwrap_or_default();
+ let mut stderr = stderr_handle
+ .and_then(|h| h.join().ok())
+ .unwrap_or_default();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/wasm-runtime-interface/src/sandbox.rs` around lines 232 - 262, The
code currently calls child.wait_timeout(timeout) before draining the child's
stdout/stderr, which can deadlock if the child blocks writing to full OS pipes;
fix by reading stdout and stderr concurrently while waiting: take child.stdout
and child.stderr (as in the existing references to child.stdout.take() /
child.stderr.take()), spawn reader threads (or async tasks) that stream each
pipe into buffers and enforce the same self.policy.max_output_bytes
limit/truncation, then call child.wait_timeout(timeout) and join the reader
threads to collect outputs; on errors, propagate SandboxError::ExecutionFailed
with the reader error text and preserve returning
SandboxError::Timeout(self.policy.max_execution_time_ms) if the wait times out,
ensuring child.kill() is still called when needed.
Summary
Adds a new
platform_sandboxhost function namespace to the WASM runtime interface, enabling WASM modules to execute sandboxed terminal commands, retrieve timestamps, and emit structured log messages to the host.Changes
New:
sandbox.rsmodulesandbox_exechost function: deserializes aSandboxExecRequestfrom WASM memory, executes the command viastd::process::Commandwith timeout enforcement (wait-timeoutcrate), captures stdout/stderr/exit_code/duration, serializes aSandboxExecResponseback into WASM memoryget_timestamphost function: returns current Unix timestamp in milliseconds, supportsRealTimeandDeterministicmodes for reproducible evaluationslog_messagehost function: reads a log message from WASM memory and forwards it to the hosttracinginfrastructure with level mapping (Trace/Debug/Info/Warn/Error)SandboxPolicystruct: security constraints including command allowlist/blocklist, max execution time, max output size, network access control, working directory, and environment variable whitelistSandboxState: runtime state tracking execution count and policy enforcementSandboxHostFunctions: implementsHostFunctionRegistrarpattern consistent with existingNetworkHostFunctionsModified:
runtime.rsInstanceConfigwithsandbox_policyandtimestamp_modefieldsRuntimeStatewithSandboxStateand counter reset supportinstantiate_with_registrarsmethod accepting multipleHostFunctionRegistrarinstances, refactoredinstantiateto delegate to itModified:
lib.rssandboxmodule and all public sandbox typesModified:
wasm_executor.rsInstanceConfigconstruction to use..Default::default()for forward compatibility with new fieldsModified:
Cargo.tomlwait-timeoutdependency for subprocess timeout handlingSummary by CodeRabbit
Release Notes
New Features
Refactor