{"record":{"id":"e6221463829421e6","repo":"astrid-runtime/astrid","slug":"private-directory-contains-traversal","errorCode":null,"errorMessage":"private directory contains traversal: {}","messagePattern":"private directory contains traversal: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/platform_fs.rs","lineNumber":708,"sourceCode":"#[cfg(unix)]\nfn open_directory_no_follow_unix(path: &Path) -> io::Result<std::fs::File> {\n    let (directory, _) = unix_directory_walk(path)?;\n    Ok(directory)\n}\n\n#[cfg(unix)]\nfn unix_directory_walk(path: &Path) -> io::Result<(std::fs::File, Vec<std::ffi::OsString>)> {\n    use nix::errno::Errno;\n    use nix::fcntl::{OFlag, openat};\n    use nix::sys::stat::Mode;\n    use std::path::Component;\n\n    let components = path.components().collect::<Vec<_>>();\n    if components\n        .iter()\n        .any(|component| matches!(component, Component::ParentDir | Component::Prefix(_)))\n    {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            format!(\"private directory contains traversal: {}\", path.display()),\n        ));\n    }\n\n    let absolute = normalize_unix_system_alias(if path.is_absolute() {\n        path.to_path_buf()\n    } else {\n        std::env::current_dir()?.join(path)\n    });\n    let mut directory = if absolute\n        .components()\n        .next()\n        .is_some_and(|component| matches!(component, Component::RootDir))\n    {\n        std::fs::File::open(\"/\")\n    } else {\n        return Err(io::Error::new(","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/platform_fs.rs#L690-L726","documentation":"unix_directory_walk resolves a path component-by-component with openat(..., O_NOFOLLOW) so symlinks and traversal cannot redirect private paths. It rejects any path containing \"..\" components or Windows-style Prefix components up front, throwing this error, because parent-directory references would let the resolved location escape the intended directory.","triggerScenarios":"Calling ensure_private_directory, validate_private_directory, or any private-file API with a path containing \"..\" (e.g. \"/home/me/../me/.astrid\"), or a path with a Windows prefix component.","commonSituations":"Untrusted or user-supplied paths containing \"..\"; path templates that splice in relative fragments; code that concatenates user input into a base directory without normalization.","solutions":["Remove \"..\" from the path by canonicalizing first: use std::fs::canonicalize and verify the result, or build absolute component-clean paths.","If the path is user-supplied, reject or normalize it before passing it to the API (the error is intentional traversal protection).","Construct paths with PathBuf::join from trusted components rather than string concatenation.","If you need an equivalent location, compute it without parent references (e.g. expand to the absolute path yourself)."],"exampleFix":"// before\nensure_private_directory(Path::new(\"/home/me/../me/.astrid\"))?;\n// after\nlet clean = std::fs::canonicalize(\"/home/me\")?.join(\".astrid\");\nensure_private_directory(&clean)?;","handlingStrategy":"validation","validationCode":"fn is_traversal_free(path: &std::path::Path) -> bool {\n    use std::path::Component;\n    path.components().all(|c| !matches!(c, Component::ParentDir | Component::Prefix(_)))\n}","typeGuard":"fn safe_path(path: &std::path::Path) -> Option<std::path::PathBuf> {\n    use std::path::Component;\n    if path.components().any(|c| matches!(c, Component::ParentDir | Component::Prefix(_))) {\n        None\n    } else { Some(path.to_path_buf()) }\n}","tryCatchPattern":"match ensure_private_directory(path) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains(\"traversal\") => {\n        eprintln!(\"rejecting traversal path: {e}\");\n    },\n    other => other?,\n}","preventionTips":["Canonicalize or normalize user-supplied paths before use","Reject \"..\" segments at input boundaries","Build paths with PathBuf::join, not string concatenation"],"tags":["filesystem","security","path-traversal","validation"],"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"}