{"record":{"id":"83d5d4df697dded2","repo":"astrid-runtime/astrid","slug":"workspace-capsule-component-path-is-unsafe","errorCode":null,"errorMessage":"workspace capsule component path is unsafe ({}): {error}","messagePattern":"workspace capsule component path is unsafe \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/lib.rs","lineNumber":1471,"sourceCode":"    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: {}\",\n                    component.path.display()\n                );\n            }\n            self.workspace_selection\n                .resolve_file(capsule_relative.join(&component.path))\n                .map_err(|error| {\n                    anyhow::anyhow!(\n                        \"workspace capsule component path is unsafe ({}): {error}\",\n                        component.path.display()\n                    )\n                })?;\n        }\n        Ok(())\n    }\n\n    /// Verify an installed capsule against the authority source selected by\n    /// this runtime. Native daemon loads use the packed System catalog; the\n    /// unbound compatibility path retains its POSIX executable check.\n    #[cfg(not(all(target_arch = \"wasm32\", target_os = \"unknown\")))]\n    fn verify_installed_authority_for_runtime(\n        &self,\n        dir: &Path,\n        manifest: &astrid_capsule_types::manifest::CapsuleManifest,\n    ) -> anyhow::Result<()> {\n        if let Some(store) = self.principal_store.as_ref() {","sourceCodeStart":1453,"sourceCodeEnd":1489,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/lib.rs#L1453-L1489","documentation":"Wraps `resolve_file` failing for one declared capsule component: the component's path, joined to the capsule-relative directory, cannot be resolved safely inside the workspace (missing file, symlink escape, or redirect). The manifest-declared path plus its display string are included for diagnosis.","triggerScenarios":"Iterating manifest components and calling resolve_file(capsule_relative.join(&component.path)) during workspace capsule verification; fails for the component whose path escapes the capsule root or does not resolve.","commonSituations":"Manifest lists a component with '../' or absolute path; component file was deleted or renamed; component is a symlink pointing outside the capsule; manifest was authored against a different capsule layout version.","solutions":["Fix the component path in the capsule manifest so it stays relative and inside the capsule directory","Restore or recreate the missing/renamed component file at the declared path","Replace any symlinked component with a real file inside the capsule tree","Reinstall the capsule from a trusted archive matching the manifest"],"exampleFix":"// before (manifest.toml)\n[[component]]\npath = \"../../shared/lib.wasm\"\n// after\n[[component]]\npath = \"components/lib.wasm\"","handlingStrategy":"validation","validationCode":"let p = capsule_relative.join(&component.path);\nif component.path.is_absolute() || p.components().any(|c| matches!(c, std::path::Component::ParentDir)) {\n    return Err(format!(\"unsafe component path: {}\", component.path.display()));\n}","typeGuard":"fn is_safe_relative(p: &Path) -> bool {\n    !p.is_absolute()\n        && p.components().all(|c| matches!(c, std::path::Component::Normal(_)))\n}","tryCatchPattern":null,"preventionTips":["Author manifests with strictly relative, Normal-component paths","Lint capsule manifests for absolute or '..' paths before publishing","Reinstall capsules rather than hand-moving component files"],"tags":["filesystem","path-safety","manifest","symlink"],"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-17T15:17:12.973Z"}