{"record":{"id":"ace1831af5481b46","repo":"zed-industries/zed","slug":"path-path-escapes-its-parent","errorCode":null,"errorMessage":"path {path:?} escapes its parent","messagePattern":"path (.+?) escapes its parent","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/extension_host/src/wasm_host.rs","lineNumber":781,"sourceCode":"    }\n\n    pub async fn writeable_path_from_extension(\n        &self,\n        id: &Arc<str>,\n        path: &Path,\n    ) -> Result<PathBuf> {\n        let canonical_work_dir = self\n            .fs\n            .canonicalize(&self.work_dir)\n            .await\n            .with_context(|| format!(\"canonicalizing work dir {:?}\", self.work_dir))?;\n        let extension_work_dir = canonical_work_dir.join(id.as_ref());\n\n        let absolute = if path.is_relative() {\n            extension_work_dir.join(path)\n        } else {\n            path.to_path_buf()\n        };\n\n        let normalized = util::paths::normalize_lexically(&absolute)\n            .map_err(|_| anyhow!(\"path {path:?} escapes its parent\"))?;\n\n        // Canonicalize the nearest existing ancestor to resolve any symlinks\n        // in the on-disk portion of the path. Components beyond that ancestor\n        // are re-appended, which lets this work for destinations that don't\n        // exist yet (e.g. nested directories created by tar extraction).\n        let mut existing = normalized.as_path();\n        let mut tail_components = Vec::new();\n        let canonical_prefix = loop {\n            match self.fs.canonicalize(existing).await {\n                Ok(canonical) => break canonical,\n                Err(_) => {\n                    if let Some(file_name) = existing.file_name() {\n                        tail_components.push(file_name.to_owned());\n                    }\n                    existing = existing","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/zed-industries/zed/blob/5a9b9558db01a6b906cec2fb70a797affdc58cdd/crates/extension_host/src/wasm_host.rs#L763-L799","documentation":"The sandboxed path check in writeable_path_from_extension rejected a path: after lexically normalizing the candidate (extension work dir joined with the requested path), the result escapes the extension's work directory via '..' components or an absolute-path trick. This is a security guard preventing a wasm extension from writing outside its assigned directory.","triggerScenarios":"Thrown at crates/extension_host/src/wasm_host.rs:772 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Fix the extension code to request paths relative to its own work dir without '..' traversal","If a path outside the work dir is legitimately needed, use the host-provided API surfaces rather than raw fs writes"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"5a9b9558db01a6b906cec2fb70a797affdc58cdd","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}