{"record":{"id":"b84ab5fc53c107ea","repo":"astrid-runtime/astrid","slug":"private-path-is-redirected","errorCode":null,"errorMessage":"private path is redirected: {}","messagePattern":"private path is redirected: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs.rs","lineNumber":425,"sourceCode":"    #[cfg(unix)]\n    {\n        verify_no_redirects_unix(path)\n    }\n\n    #[cfg(not(any(unix, windows)))]\n    {\n        let _ = path;\n        Ok(())\n    }\n}\n\n#[cfg(unix)]\nfn verify_no_redirects_unix(path: &Path) -> io::Result<()> {\n    use nix::fcntl::{OFlag, openat};\n    use nix::sys::stat::Mode;\n\n    match std::fs::symlink_metadata(path) {\n        Ok(metadata) if metadata.file_type().is_symlink() => Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"private path is redirected: {}\", path.display()),\n        )),\n        Ok(metadata) if metadata.is_dir() => open_directory_no_follow_unix(path).map(drop),\n        Ok(_) => {\n            let parent = path.parent().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"private path has no parent: {}\", path.display()),\n                )\n            })?;\n            let name = path.file_name().ok_or_else(|| {\n                io::Error::new(\n                    io::ErrorKind::InvalidInput,\n                    format!(\"private path has no file name: {}\", path.display()),\n                )\n            })?;\n            let directory = open_directory_no_follow_unix(parent)?;","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs.rs#L407-L443","documentation":"verify_no_redirects_unix refuses to operate on a path whose final component is a symbolic link, since a symlink could redirect security-sensitive private data outside the intended owner-only location. It fails with io::ErrorKind::InvalidData and the offending path in the message.","triggerScenarios":"Calling verify_no_redirects (Unix) with a path whose symlink_metadata reports a symlink file type — e.g. ~/.astrid or a child replaced by a symlink to elsewhere.","commonSituations":"Backup/restore tools that recreated state as symlinks; dotfile managers (stow, chezmoi) symlinking ~/.astrid into a repo; an attacker-planted symlink in a shared directory.","solutions":["Remove the symlink and replace it with a real directory/file (e.g. rsync -L or cp -rL to materialize the target).","Point your configuration at the real location instead of a symlinked path.","Re-run ensure_private_directory to rebuild the private path as a real owner-only directory."],"exampleFix":"// before (shell)\nln -s /mnt/data/.astrid ~/.astrid\n// after (shell)\nrm ~/.astrid && cp -rL /mnt/data/.astrid ~/.astrid && chmod 700 ~/.astrid","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(&path)?;\nif meta.file_type().is_symlink() {\n    return Err(format!(\"{} is a symlink; remove it before use\", path.display()));\n}","typeGuard":"fn is_no_symlink(p: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| !m.file_type().is_symlink()).unwrap_or(false)\n}","tryCatchPattern":"if let Err(e) = verify_no_redirects(&path) {\n    if e.kind() == io::ErrorKind::InvalidData {\n        eprintln!(\"remove the symlink and materialize the real data: {e}\");\n    }\n    return Err(e);\n}","preventionTips":["Never symlink ~/.astrid or its children (dotfile managers: use real dirs or bind mounts)","After restore/backup, verify no symlinks were introduced into the private tree","Copy symlink targets (cp -rL) rather than preserving links"],"tags":["io","filesystem","symlink","security"],"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"}