{"record":{"id":"85c90d00759d5077","repo":"astrid-runtime/astrid","slug":"workspace-capsule-tree-contains-an-unsafe-redirect","errorCode":null,"errorMessage":"workspace capsule tree contains an unsafe redirect: {error}","messagePattern":"workspace capsule tree contains an unsafe redirect: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1446,"sourceCode":"        .with_identity_store(Arc::clone(&kernel.identity_store))\n        .with_access_resolver(access_resolver);\n        drop(astrid_runtime::spawn(dispatcher.run()));\n\n        debug_assert_eq!(\n            kernel.event_bus.subscriber_count(),\n            INTERNAL_SUBSCRIBER_COUNT,\n            \"INTERNAL_SUBSCRIBER_COUNT is stale; update it when adding permanent subscribers\"\n        );\n\n        Ok(kernel)\n    }\n\n    fn verify_workspace_capsule_tree(&self, dir: &Path) -> anyhow::Result<()> {\n        if let Ok(relative) = dir.strip_prefix(self.workspace_selection.state_dir()) {\n            self.workspace_selection\n                .verify_tree(relative)\n                .map_err(|error| {\n                    anyhow::anyhow!(\"workspace capsule tree contains an unsafe redirect: {error}\")\n                })?;\n        }\n        Ok(())\n    }\n\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    fn verify_workspace_component_paths(\n        &self,\n        dir: &Path,\n        manifest: &astrid_capsule_types::manifest::CapsuleManifest,\n    ) -> anyhow::Result<()> {\n        let Ok(capsule_relative) = dir.strip_prefix(self.workspace_selection.state_dir()) else {\n            return Ok(());\n        };\n        for component in &manifest.components {\n            if component.path.is_absolute() {\n                anyhow::bail!(\n                    \"workspace capsule component must be relative: {}\",","sourceCodeStart":1428,"sourceCodeEnd":1464,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1428-L1464","documentation":"Wraps a failure from `verify_tree` when checking that a directory inside the workspace state dir contains no symlinks or path components that redirect outside the tree. The kernel refuses to traverse a capsule tree that could be a symlink-escape vector. It is a security guard, not a functional bug.","triggerScenarios":"Calling workspace capsule verification (verify_workspace_capsule_tree) when `dir` sits under workspace_selection.state_dir() and verify_tree finds a symlink or redirect component inside the relative tree.","commonSituations":"A capsule was extracted with symlinks preserved from an archive; a user hand-linked cache/state directories to another disk; a previous partial install left dangling links; shared state dirs synced via tools that materialize symlinks.","solutions":["Find the offending entry reported by the inner error and replace the symlink with a real file/directory","Re-extract or reinstall the capsule archive with symlink preservation disabled (e.g. tar --no-same-owner without -h)","Remove the stale capsule directory under the workspace state dir and let the kernel re-materialize it","Inspect verify_tree in astrid-kernel to confirm which path component failed before deleting data"],"exampleFix":"// before: capsule tree contains symlink\nstate_dir/capsules/foo -> /etc/something\n// after\nrm state_dir/capsules/foo && mkdir state_dir/capsules/foo && # re-extract real files into it","handlingStrategy":"validation","validationCode":"let rel = dir.strip_prefix(workspace_selection.state_dir())?;\nif let Err(e) = workspace_selection.verify_tree(rel) {\n    // reject before handing to kernel\n    return Err(e);\n}","typeGuard":"fn is_under_state_dir(dir: &Path, state_dir: &Path) -> bool {\n    dir.strip_prefix(state_dir).is_ok()\n}","tryCatchPattern":null,"preventionTips":["Extract capsule archives with symlink preservation disabled","Never hand-create symlinks inside the workspace state dir","Use workspace tooling (not manual copies) to populate capsule trees"],"tags":["filesystem","path-safety","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"}