{"record":{"id":"58adba9748d252d3","repo":"astrid-runtime/astrid","slug":"workspace-tree-contains-a-redirected-or-special-en","errorCode":null,"errorMessage":"workspace tree contains a redirected or special entry: {}","messagePattern":"workspace tree contains a redirected or special entry: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/workspace_security.rs","lineNumber":151,"sourceCode":"    /// Returns an error if the root is unsafe, any descendant is a symlink,\n    /// reparse redirect, or special file, or the tree changes while walking.\n    pub fn verify_tree(&self, relative: impl AsRef<Path>) -> io::Result<PathBuf> {\n        let relative = relative.as_ref();\n        let root = self.resolve_directory(relative)?;\n        if !root.exists() {\n            return Ok(root);\n        }\n        let mut pending = vec![root.clone()];\n        while let Some(dir) = pending.pop() {\n            for entry in std::fs::read_dir(&dir)? {\n                let entry = entry?;\n                let path = entry.path();\n                let metadata = std::fs::symlink_metadata(&path)?;\n                if metadata.file_type().is_symlink()\n                    || (!metadata.is_dir() && !metadata.is_file())\n                    || std::fs::canonicalize(&path)? != path\n                {\n                    return Err(io::Error::new(\n                        io::ErrorKind::InvalidInput,\n                        format!(\n                            \"workspace tree contains a redirected or special entry: {}\",\n                            path.display()\n                        ),\n                    ));\n                }\n                if metadata.is_dir() {\n                    pending.push(path);\n                }\n            }\n        }\n        self.resolve_directory(relative)?;\n        Ok(root)\n    }\n\n    fn resolve_descendant(&self, relative: &Path, kind: DescendantKind) -> io::Result<PathBuf> {\n        self.verify()?;","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/workspace_security.rs#L133-L169","documentation":"verify_tree walks the workspace tree and rejects any entry that is a symlink, is neither a regular file nor a directory, or whose canonicalized path differs from its walked path. This InvalidInput guards against symlink-based escapes and special files (FIFOs, devices, sockets) appearing inside the workspace, which could redirect reads/writes outside the trusted root.","triggerScenarios":"Calling verify (or an operation that validates the tree) when the workspace contains a symlink, a FIFO/socket/device, or any entry whose canonical path diverges (e.g. a hardlinked bind or a path containing '..' resolved through a symlinked parent).","commonSituations":"Checkouts that include symlinked dependencies (node_modules links, vendored symlinks); build systems creating FIFOs; a colleague or tool commiting symlinks pointing outside the repo; mounts appearing inside the tree during validation.","solutions":["Remove or replace symlinks inside the workspace tree with real files/directories or copies.","Delete or relocate special files (FIFOs, sockets, device nodes) out of the workspace.","Find the offending entry named in the error and check why it canonicalizes elsewhere (readlink/find -type l).","Configure tools that create links (package managers, build scripts) to use copies or hardlink-free modes within the workspace."],"exampleFix":"// before: workspace contains a symlink\n// mylib -> /opt/shared/mylib\n// after\ngit rm mylib\ncp -rL /opt/shared/mylib mylib  # materialize a real copy","handlingStrategy":"validation","validationCode":"fn tree_clean(dir: &Path) -> std::io::Result<()> {\n    for entry in std::fs::read_dir(dir)? {\n        let p = entry?.path();\n        let m = std::fs::symlink_metadata(&p)?;\n        if m.file_type().is_symlink() || (!m.is_dir() && !m.is_file()) {\n            return Err(io::Error::new(io::ErrorKind::InvalidInput, \"bad entry\"));\n        }\n        if m.is_dir() { tree_clean(&p)?; }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never commit symlinks or special files into workspace trees","Configure package managers/build tools to copy instead of symlink inside the workspace","Run find <root> -type l periodically to detect stray links"],"tags":["security","symlink","filesystem","path-traversal"],"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-14T05:17:10.506Z"}