{"record":{"id":"1bd2ff8a81a7fd9c","repo":"astrid-runtime/astrid","slug":"projected-path-is-redirected-or-not-a-regular-file","errorCode":null,"errorMessage":"projected path is redirected or not a regular file: {}","messagePattern":"projected path is redirected or not a regular file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1673,"sourceCode":"                }\n            }\n            Ok(())\n        }\n\n        let mut inventory = ProjectionInventory::default();\n        walk(root, root, &mut inventory)?;\n        Ok(inventory)\n    }\n\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    fn read_projection_file_nofollow(path: &Path) -> anyhow::Result<Vec<u8>> {\n        use std::io::Read as _;\n\n        let metadata = std::fs::symlink_metadata(path).map_err(|error| {\n            anyhow::anyhow!(\"inspect projected file {}: {error}\", path.display())\n        })?;\n        if metadata.file_type().is_symlink() || !metadata.is_file() {\n            anyhow::bail!(\n                \"projected path is redirected or not a regular file: {}\",\n                path.display()\n            );\n        }\n        let mut file = open_projection_file_nofollow(path)?;\n        let mut bytes = Vec::new();\n        file.read_to_end(&mut bytes)\n            .map_err(|error| anyhow::anyhow!(\"read projected file {}: {error}\", path.display()))?;\n        if file.metadata()?.len() != metadata.len() || bytes.len() as u64 != metadata.len() {\n            anyhow::bail!(\"projected file changed while read: {}\", path.display());\n        }\n        Ok(bytes)\n    }\n\n    /// Load a capsule into the Kernel from a directory containing a Capsule.toml\n    ///\n    /// # Errors\n    ///","sourceCodeStart":1655,"sourceCodeEnd":1691,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1655-L1691","documentation":"This error means a projected path failed the read-time safety check: symlink_metadata showed either a symlink (a redirect) or a non-regular file where a regular file was required. The kernel throws it in the per-file read path so it never opens anything that could redirect (TOCTOU via symlink) or block (e.g. a FIFO), and is the single-file counterpart to the projection-wide symlink/special-file checks.","triggerScenarios":"Reading a projected file (the read_projected_file path used during inventory verification) when metadata.file_type().is_symlink() is true or metadata.is_file() is false — e.g. the entry was replaced by a symlink between inventory and read, or the caller passed a path to a directory/socket/fifo.","commonSituations":"Concurrent modification of the capsule directory (another process swapping in symlinks); passing the wrong path (a directory) to a file-read API; a projection where a packaging bug left links or special files; an attacker redirecting a projected path via a race.","solutions":["Re-materialize the capsule so the projection is rebuilt from the trusted archive with only regular files","Verify the path passed to the read API is a regular file inside the projection (check symlink_metadata before calling)","Find and remove whatever replaced the file with a symlink/special file; investigate concurrent writers to the capsule directory","If packaging produced links/special files, repackage (dereference links, exclude non-regular entries)"],"exampleFix":"// before: reading without checking the entry type\nlet bytes = read_projected_file(&path)?;\n\n// after: pre-check before reading\nlet meta = std::fs::symlink_metadata(&path)?;\nif meta.file_type().is_symlink() || !meta.is_file() {\n    anyhow::bail!(\"refusing non-regular projected path: {}\", path.display());\n}\nlet bytes = read_projected_file(&path)?;","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(path)?;\nif meta.file_type().is_symlink() || !meta.is_file() {\n    return Err(\"projected path must be a regular, non-symlink file\");\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"redirected or not a regular file\") => {\n        // re-materialize the projection; treat as a security signal\n    }\n    other => other?,\n}","preventionTips":["Pre-check entry type with symlink_metadata before reading","Never share capsule directories with untrusted writers","Re-materialize from the trusted archive if the projection was touched","Treat any symlink appearance in a projection as a potential race/attack"],"tags":["filesystem","security","symlink","toctou","capsule"],"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"}