{"record":{"id":"a39efc444a2d64e9","repo":"astrid-runtime/astrid","slug":"capsule-projection-contains-a-symbolic-link","errorCode":null,"errorMessage":"capsule projection contains a symbolic link: {}","messagePattern":"capsule projection contains a symbolic link: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1640,"sourceCode":"        ) -> anyhow::Result<()> {\n            for entry in std::fs::read_dir(directory).map_err(|error| {\n                anyhow::anyhow!(\"read capsule projection {}: {error}\", directory.display())\n            })? {\n                let entry = entry\n                    .map_err(|error| anyhow::anyhow!(\"read capsule projection entry: {error}\"))?;\n                let path = entry.path();\n                let relative = path.strip_prefix(root).map_err(|_| {\n                    anyhow::anyhow!(\"capsule projection escaped its root: {}\", path.display())\n                })?;\n                let relative_text = relative.to_str().ok_or_else(|| {\n                    anyhow::anyhow!(\"capsule projection path is not UTF-8: {}\", path.display())\n                })?;\n                let metadata = std::fs::symlink_metadata(&path).map_err(|error| {\n                    anyhow::anyhow!(\"inspect capsule projection {}: {error}\", path.display())\n                })?;\n                let file_type = metadata.file_type();\n                if file_type.is_symlink() {\n                    anyhow::bail!(\n                        \"capsule projection contains a symbolic link: {}\",\n                        path.display()\n                    );\n                }\n                if file_type.is_dir() {\n                    inventory.directories.insert(relative_text.to_owned());\n                    walk(root, &path, inventory)?;\n                } else if file_type.is_file() {\n                    inventory.files.insert(relative_text.to_owned());\n                } else {\n                    anyhow::bail!(\n                        \"capsule projection contains a special file: {}\",\n                        path.display()\n                    );\n                }\n            }\n            Ok(())\n        }","sourceCodeStart":1622,"sourceCodeEnd":1658,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1622-L1658","documentation":"This error means the walk of the capsule's projected directory found a symbolic link, which the kernel forbids: symlinks can redirect reads outside the capsule projection and bypass its containment guarantees. The library throws it during projection inventory so that any capsule whose on-disk projection contains a symlink is rejected rather than traversed.","triggerScenarios":"Inventorying a projection (the recursive walk over the materialized capsule directory) when std::fs::symlink_metadata reports a symlink at any path within the projection — e.g. the capsule archive contained a symlink, or something on the host placed one inside the materialized directory.","commonSituations":"Packaging a capsule on macOS/Linux where a build step created symlinks (e.g. node_modules, vendored deps) and tar preserved them; extracting an archive with symlink-preserving flags; an attacker or misconfigured script planting symlinks in the capsule output directory.","solutions":["Repackage the capsule so it contains only regular files and directories (dereference symlinks at packaging time, e.g. tar -h / cp -L)","Find the offending link (the path in the error) and replace it with a real copy of its target, or remove it","Fix the build pipeline to not emit symlinks into the capsule output (e.g. configure the bundler to inline/duplicate linked files)","If the symlink appeared post-materialization, investigate what wrote into the capsule directory and re-materialize"],"exampleFix":"# before: packaging preserves symlinks\ntar -cf capsule.capsule -C build .\n\n# after: dereference symlinks into regular files\ntar -h -cf capsule.capsule -C build .","handlingStrategy":"validation","validationCode":"fn projection_has_symlinks(root: &Path) -> anyhow::Result<bool> {\n    for entry in walkdir::WalkDir::new(root).follow_links(false) {\n        if entry?.path_is_symlink() { return Ok(true); }\n    }\n    Ok(false)\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"symbolic link\") => {\n        // clean the offending path and repackage\n    }\n    other => other?,\n}","preventionTips":["Package capsules with symlink dereferencing (tar -h, cp -L)","Reject symlinks in build output as a CI check","Configure bundlers to inline or duplicate linked files","Inspect extracted archives for links before materializing"],"tags":["filesystem","security","symlink","capsule","path-safety"],"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"}