zeroclaw-labs/zeroclaw · error · anyhow::Error
runtime.wasm.tools_dir must not contain '..' path traversal
Error message
runtime.wasm.tools_dir must not contain '..' path traversal
What it means
Final WASM config guard: tools_dir must not contain '..' because it is joined onto the workspace root to resolve module paths (tools_dir()), and '..' segments would let the resolved path escape the workspace sandbox. This is a path-traversal containment check on untrusted config input — even a module name cannot then reach outside the workspace tree.
Source
Thrown at crates/zeroclaw-runtime/src/platform/wasm.rs:81
}
/// Validate the WASM config for common misconfigurations.
pub fn validate_config(&self) -> Result<()> {
if self.config.memory_limit_mb == 0 {
bail!("runtime.wasm.memory_limit_mb must be > 0");
}
if self.config.memory_limit_mb > 4096 {
bail!(
"runtime.wasm.memory_limit_mb of {} exceeds the 4 GB safety limit for 32-bit WASM",
self.config.memory_limit_mb
);
}
if self.config.tools_dir.is_empty() {
bail!("runtime.wasm.tools_dir cannot be empty");
}
// Verify tools directory doesn't escape workspace
if self.config.tools_dir.contains("..") {
bail!("runtime.wasm.tools_dir must not contain '..' path traversal");
}
Ok(())
}
/// Resolve the absolute path to the WASM tools directory.
pub fn tools_dir(&self, workspace_dir: &Path) -> PathBuf {
workspace_dir.join(&self.config.tools_dir)
}
/// Build capabilities from config defaults.
pub fn default_capabilities(&self) -> WasmCapabilities {
WasmCapabilities {
read_workspace: self.config.allow_workspace_read,
write_workspace: self.config.allow_workspace_write,
allowed_hosts: self.config.allowed_hosts.clone(),
fuel_override: 0,
memory_override_mb: 0,
}View on GitHub (pinned to 88bb9c8533)
Solutions
- Use a directory inside the workspace: tools_dir = "tools/wasm"
- To share modules between workspaces, copy or symlink the directory inside each workspace (a symlink inside is still contained) and keep the config traversal-free
- Move the artifact build output into the workspace (adjust your build to emit into <workspace>/tools/wasm)
- Never encode '..' in the config; the check is a substring match, so even a legitimate-looking '../x' is refused
Example fix
# before [runtime.wasm] tools_dir = "../shared/wasm" # after # ln -s /srv/shared/wasm <workspace>/tools/wasm [runtime.wasm] tools_dir = "tools/wasm"
Defensive patterns
Strategy: validation
Validate before calling
if cfg.tools_dir.contains("..") {
return Err("tools_dir must stay inside the workspace".into());
// stronger: canonicalize and assert the resolved path starts with the workspace root
}
let resolved = workspace_dir.join(&cfg.tools_dir).canonicalize()?;
if !resolved.starts_with(workspace_dir.canonicalize()?) {
return Err("tools_dir escapes workspace".into());
} Try / catch
if let Err(e) = platform.validate_config() {
if e.to_string().contains("path traversal") {
// security guard: reconfigure to an in-workspace directory; never strip the check
}
} Prevention
- Keep tool artifacts inside each workspace; use symlinks inside the workspace for sharing
- Validate config-sourced paths by canonicalizing against the workspace root, not just substring checks
- Treat traversal attempts in submitted configs as hostile input
When it happens
Trigger: Setting tools_dir = "../shared/wasm" to point at a directory outside the workspace; ".." style leftovers from templating; configs written by users assuming absolute-ish traversal is allowed.
Common situations: Trying to share one tools directory between multiple workspaces via traversal; monorepo layouts where the wasm artifacts live above the agent workspace; porting configs from setups that permitted absolute paths.
Related errors
- runtime.wasm.memory_limit_mb must be > 0
- runtime.wasm.memory_limit_mb of {} exceeds the 4 GB safety l
- runtime.wasm.tools_dir cannot be empty
- security.otp.cache_valid_secs must be greater than or equal
- security.otp.gated_actions[{i}] contains invalid characters:
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/5f79fe72b04089d3.
Report an issue: GitHub.