{"record":{"id":"ba699f253046d04a","repo":"astrid-runtime/astrid","slug":"path","errorCode":null,"errorMessage":"{path}","messagePattern":"\\{path\\}","errorType":"exception","errorClass":"VfsError::Io(std::io::Error::IsADirectory)","httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule/src/engine/wasm/storage_vfs.rs","lineNumber":549,"sourceCode":"            .write()\n            .await\n            .remove(handle)\n            .ok_or(VfsError::InvalidHandle)?;\n        let file = file.lock().await;\n        if file.writable && file.dirty {\n            self.filesystem\n                .write(&file.path, &file.bytes)\n                .map_err(map_filesystem_error)?;\n        }\n        Ok(())\n    }\n}\n\nfn map_filesystem_error(error: FilesystemError) -> VfsError {\n    match error {\n        FilesystemError::InvalidPath(path) => VfsError::SandboxViolation(path),\n        FilesystemError::NotFound(path) => VfsError::NotFound(path.as_str().to_owned()),\n        FilesystemError::IsDirectory(path) => VfsError::Io(std::io::Error::new(\n            std::io::ErrorKind::IsADirectory,\n            path.as_str().to_owned(),\n        )),\n        FilesystemError::NotDirectory(path) => VfsError::Io(std::io::Error::new(\n            std::io::ErrorKind::NotADirectory,\n            path.as_str().to_owned(),\n        )),\n        FilesystemError::AlreadyExists(path) => VfsError::Io(std::io::Error::new(\n            std::io::ErrorKind::AlreadyExists,\n            path.as_str().to_owned(),\n        )),\n        FilesystemError::DirectoryNotEmpty(path) => VfsError::Io(std::io::Error::new(\n            std::io::ErrorKind::DirectoryNotEmpty,\n            path.as_str().to_owned(),\n        )),\n        FilesystemError::NamespaceConflict(path) => VfsError::Io(std::io::Error::other(format!(\n            \"namespace conflict at {}\",\n            path.as_str()","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule/src/engine/wasm/storage_vfs.rs#L531-L567","documentation":"map_filesystem_error in the capsule WASM storage VFS converts the inner FilesystemError::InvalidPath into VfsError::SandboxViolation, carrying the offending path as the message. This fires when a guest path escapes or violates the sandbox prefix rules checked by ensure_prefix — the VFS refuses to resolve any path outside the capsule's mounted prefix.","triggerScenarios":"Calling file_entry, exists, mkdir, open_dir, or opening a file from WASM guest code with a path that fails prefix validation: absolute paths outside the mount, '..' traversal, empty or malformed relative paths, or Windows-style paths when not permitted.","commonSituations":"WASM module hardcoding '/etc/passwd' or 'C:\\\\data' paths instead of the sandbox-relative mount path; path joins accidentally producing '../' segments; passing raw user input straight into VFS path arguments.","solutions":["Rewrite the guest path to be relative to the sandbox mount prefix.","Strip or reject '..' components and absolute prefixes before calling the VFS API.","Check the configured mount/prefix mapping so the path lands inside it.","Validate user-supplied paths with the same prefix rules before passing them to the VFS."],"exampleFix":"// before\nvfs.exists(\"/etc/hosts\")  // SandboxViolation\n\n// after\nvfs.exists(\"data/hosts\") // path relative to sandbox prefix","handlingStrategy":"validation","validationCode":"// Rust (caller-side prefix check)\nfn path_in_sandbox(prefix: &str, path: &str) -> bool {\n    let p = std::path::Path::new(path);\n    !p.is_absolute()\n        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n        && path.starts_with(prefix)\n}","typeGuard":"fn is_safe_vfs_path(path: &str) -> bool {\n    !path.is_empty()\n        && !path.starts_with('/')\n        && !path.split('/').any(|seg| seg == \"..\" || seg.is_empty() || seg == \".\")\n}","tryCatchPattern":"match vfs_result {\n    Err(VfsError::SandboxViolation(path)) => {\n        eprintln!(\"path rejected by sandbox: {path}\");\n        // rewrite path relative to mount prefix and retry\n    }\n    other => other?,\n}","preventionTips":["Always build VFS paths relative to the mount prefix","Never pass raw user input directly as a VFS path","Normalize and reject '..' segments before calling VFS APIs","Document the sandbox prefix for guest module authors"],"tags":["wasm","sandbox","path-traversal","vfs"],"backgroundTag":"path-traversal-blocked","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}