{"record":{"id":"5f79fe72b04089d3","repo":"zeroclaw-labs/zeroclaw","slug":"runtime-wasm-tools-dir-must-not-contain-path","errorCode":null,"errorMessage":"runtime.wasm.tools_dir must not contain '..' path traversal","messagePattern":"runtime\\.wasm\\.tools_dir must not contain '\\.\\.' path traversal","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/platform/wasm.rs","lineNumber":81,"sourceCode":"    }\n\n    /// Validate the WASM config for common misconfigurations.\n    pub fn validate_config(&self) -> Result<()> {\n        if self.config.memory_limit_mb == 0 {\n            bail!(\"runtime.wasm.memory_limit_mb must be > 0\");\n        }\n        if self.config.memory_limit_mb > 4096 {\n            bail!(\n                \"runtime.wasm.memory_limit_mb of {} exceeds the 4 GB safety limit for 32-bit WASM\",\n                self.config.memory_limit_mb\n            );\n        }\n        if self.config.tools_dir.is_empty() {\n            bail!(\"runtime.wasm.tools_dir cannot be empty\");\n        }\n        // Verify tools directory doesn't escape workspace\n        if self.config.tools_dir.contains(\"..\") {\n            bail!(\"runtime.wasm.tools_dir must not contain '..' path traversal\");\n        }\n        Ok(())\n    }\n\n    /// Resolve the absolute path to the WASM tools directory.\n    pub fn tools_dir(&self, workspace_dir: &Path) -> PathBuf {\n        workspace_dir.join(&self.config.tools_dir)\n    }\n\n    /// Build capabilities from config defaults.\n    pub fn default_capabilities(&self) -> WasmCapabilities {\n        WasmCapabilities {\n            read_workspace: self.config.allow_workspace_read,\n            write_workspace: self.config.allow_workspace_write,\n            allowed_hosts: self.config.allowed_hosts.clone(),\n            fuel_override: 0,\n            memory_override_mb: 0,\n        }","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/platform/wasm.rs#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n[runtime.wasm]\ntools_dir = \"../shared/wasm\"\n\n# after\n# ln -s /srv/shared/wasm <workspace>/tools/wasm\n[runtime.wasm]\ntools_dir = \"tools/wasm\"","handlingStrategy":"validation","validationCode":"if cfg.tools_dir.contains(\"..\") {\n    return Err(\"tools_dir must stay inside the workspace\".into());\n// stronger: canonicalize and assert the resolved path starts with the workspace root\n}\nlet resolved = workspace_dir.join(&cfg.tools_dir).canonicalize()?;\nif !resolved.starts_with(workspace_dir.canonicalize()?) {\n    return Err(\"tools_dir escapes workspace\".into());\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = platform.validate_config() {\n    if e.to_string().contains(\"path traversal\") {\n        // security guard: reconfigure to an in-workspace directory; never strip the check\n    }\n}","preventionTips":["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"],"tags":["rust","zeroclaw","wasm","security","path-traversal","config"],"backgroundTag":"path-traversal","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}