{"record":{"id":"9493defa6860afdb","repo":"astrid-runtime/astrid","slug":"principal-store-capability-entry-is-redirected-or","errorCode":null,"errorMessage":"principal-store capability entry is redirected or not a directory","messagePattern":"principal-store capability entry is redirected or not a directory","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-storage/src/engine/durable/native_io.rs","lineNumber":96,"sourceCode":"                .map_err(|source| {\n                    io_error(\"create principal-store capability directory\", source)\n                })?;\n            sync_directory(parent)?;\n            open()\n                .map(Some)\n                .map_err(|source| io_error(\"open principal-store capability directory\", source))\n        },\n        Err(source) => Err(io_error(\n            \"open principal-store capability directory\",\n            source,\n        )),\n    }\n}\n\nfn validate_directory_entry(parent: &Dir, name: &Path) -> io::Result<()> {\n    let metadata = parent.symlink_metadata(name)?;\n    if !metadata.is_dir() || directory_entry_is_redirected(&metadata) {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"principal-store capability entry is redirected or not a directory\",\n        ));\n    }\n    Ok(())\n}\n\n#[cfg(windows)]\nfn directory_entry_is_redirected(metadata: &cap_std::fs::Metadata) -> bool {\n    use cap_std::fs::MetadataExt as _;\n    use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;\n\n    metadata.file_type().is_symlink()\n        || metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0\n}\n\n#[cfg(not(windows))]\nfn directory_entry_is_redirected(metadata: &cap_std::fs::Metadata) -> bool {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage/src/engine/durable/native_io.rs#L78-L114","documentation":"validate_directory_entry uses symlink_metadata to check that a principal-store directory entry is a real directory and not a symlink or other redirected entry. If the entry is a symlink, or otherwise not a directory, the library refuses to open it. This enforces that capability-relative paths only traverse genuine directories.","triggerScenarios":"Calling open_directory where the named entry is a symlink (possibly to outside the store), a regular file, or another non-directory object; also triggered when filesystem-level redirection (e.g. overlay/symlink tricks) makes the entry look redirected to directory_entry_is_redirected.","commonSituations":"A developer replaced a store subdirectory with a symlink to share data between environments; automated tooling linked directories to save space; an attacker planted a symlink inside a world-writable store root.","solutions":["Replace the symlink with a real directory (rm the link, mkdir the directory) and restore/migrate its contents","Do not symlink any entries inside the principal-store root","Check permissions on the store root so untrusted users cannot plant symlinks","Run any store-integrity/repair tooling the library provides to rebuild the directory layout"],"exampleFix":"// before\nln -s /shared/data store/principals/tenant-a\n// after\nrm store/principals/tenant-a && mkdir store/principals/tenant-a","handlingStrategy":"validation","validationCode":"fn assert_real_dir(p: &Path) -> io::Result<()> {\n    let md = p.symlink_metadata()?;\n    if md.file_type().is_symlink() || !md.is_dir() {\n        return Err(io::Error::new(io::ErrorKind::InvalidData, \"entry is symlink or not a dir\"));\n    }\n    Ok(())\n}","typeGuard":"fn is_plain_dir(p: &Path) -> bool {\n    std::fs::symlink_metadata(p).map(|m| m.is_dir() && !m.file_type().is_symlink()).unwrap_or(false)\n}","tryCatchPattern":"match open_directory_result {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        // replace symlink with real directory, then retry\n    }\n    r => r,\n}","preventionTips":["Never symlink entries inside the principal-store root","Set store directories to owner-only permissions","Run integrity scans that flag symlinks before opening the store","Avoid tools that restructure store layout with links"],"tags":["io","filesystem","symlink","security"],"backgroundTag":"symlink-detected-in-path","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"}